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); 2665b84ba26STejun Heo cancel_delayed_work_sync(&spec->vt1708_hp_work); 2671f2e99feSLydia Wang } 268f5271101SLydia Wang 26925eaba2fSLydia Wang 270f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 271f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 272f5271101SLydia Wang { 273f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 274f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 275f5271101SLydia Wang 276f5271101SLydia Wang set_jack_power_state(codec); 277f5271101SLydia Wang analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); 2781f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 2791f2e99feSLydia Wang if (is_aa_path_mute(codec)) 2801f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 2811f2e99feSLydia Wang else 2821f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 2831f2e99feSLydia Wang } 284f5271101SLydia Wang return change; 285f5271101SLydia Wang } 286f5271101SLydia Wang 287f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 288f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 289f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 290f5271101SLydia Wang .name = NULL, \ 291f5271101SLydia Wang .index = 0, \ 292f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 293f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 294f5271101SLydia Wang .put = analog_input_switch_put, \ 295f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 296f5271101SLydia Wang 29725eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec); 29825eaba2fSLydia Wang 29925eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, 30025eaba2fSLydia Wang struct snd_ctl_elem_value *ucontrol) 30125eaba2fSLydia Wang { 30225eaba2fSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 30325eaba2fSLydia Wang struct via_spec *spec = codec->spec; 30425eaba2fSLydia Wang int i; 30525eaba2fSLydia Wang int change = 0; 30625eaba2fSLydia Wang 30725eaba2fSLydia Wang long *valp = ucontrol->value.integer.value; 30825eaba2fSLydia Wang int lmute, rmute; 30925eaba2fSLydia Wang if (strstr(kcontrol->id.name, "Switch") == NULL) { 31025eaba2fSLydia Wang snd_printd("Invalid control!\n"); 31125eaba2fSLydia Wang return change; 31225eaba2fSLydia Wang } 31325eaba2fSLydia Wang change = snd_hda_mixer_amp_switch_put(kcontrol, 31425eaba2fSLydia Wang ucontrol); 31525eaba2fSLydia Wang /* Get mute value */ 31625eaba2fSLydia Wang lmute = *valp ? 0 : HDA_AMP_MUTE; 31725eaba2fSLydia Wang valp++; 31825eaba2fSLydia Wang rmute = *valp ? 0 : HDA_AMP_MUTE; 31925eaba2fSLydia Wang 32025eaba2fSLydia Wang /* Set hp pins */ 32125eaba2fSLydia Wang if (!spec->hp_independent_mode) { 32225eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 32325eaba2fSLydia Wang snd_hda_codec_amp_update( 32425eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 32525eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 32625eaba2fSLydia Wang lmute); 32725eaba2fSLydia Wang snd_hda_codec_amp_update( 32825eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 32925eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 33025eaba2fSLydia Wang rmute); 33125eaba2fSLydia Wang } 33225eaba2fSLydia Wang } 33325eaba2fSLydia Wang 33425eaba2fSLydia Wang if (!lmute && !rmute) { 33525eaba2fSLydia Wang /* Line Outs */ 33625eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 33725eaba2fSLydia Wang snd_hda_codec_amp_stereo( 33825eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 33925eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 34025eaba2fSLydia Wang /* Speakers */ 34125eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 34225eaba2fSLydia Wang snd_hda_codec_amp_stereo( 34325eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], 34425eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 34525eaba2fSLydia Wang /* unmute */ 34625eaba2fSLydia Wang via_hp_bind_automute(codec); 34725eaba2fSLydia Wang 34825eaba2fSLydia Wang } else { 34925eaba2fSLydia Wang if (lmute) { 35025eaba2fSLydia Wang /* Mute all left channels */ 35125eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 35225eaba2fSLydia Wang snd_hda_codec_amp_update( 35325eaba2fSLydia Wang codec, 35425eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 35525eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 35625eaba2fSLydia Wang lmute); 35725eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 35825eaba2fSLydia Wang snd_hda_codec_amp_update( 35925eaba2fSLydia Wang codec, 36025eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 36125eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 36225eaba2fSLydia Wang lmute); 36325eaba2fSLydia Wang } 36425eaba2fSLydia Wang if (rmute) { 36525eaba2fSLydia Wang /* mute all right channels */ 36625eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 36725eaba2fSLydia Wang snd_hda_codec_amp_update( 36825eaba2fSLydia Wang codec, 36925eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 37025eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 37125eaba2fSLydia Wang rmute); 37225eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 37325eaba2fSLydia Wang snd_hda_codec_amp_update( 37425eaba2fSLydia Wang codec, 37525eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 37625eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 37725eaba2fSLydia Wang rmute); 37825eaba2fSLydia Wang } 37925eaba2fSLydia Wang } 38025eaba2fSLydia Wang return change; 38125eaba2fSLydia Wang } 38225eaba2fSLydia Wang 38325eaba2fSLydia Wang #define BIND_PIN_MUTE \ 38425eaba2fSLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 38525eaba2fSLydia Wang .name = NULL, \ 38625eaba2fSLydia Wang .index = 0, \ 38725eaba2fSLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 38825eaba2fSLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 38925eaba2fSLydia Wang .put = bind_pin_switch_put, \ 39025eaba2fSLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 39125eaba2fSLydia Wang 39271eb7dccSLydia Wang static struct snd_kcontrol_new via_control_templates[] = { 393c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 394c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 395f5271101SLydia Wang ANALOG_INPUT_MUTE, 39625eaba2fSLydia Wang BIND_PIN_MUTE, 397c577b8a1SJoseph Chan }; 398c577b8a1SJoseph Chan 399c577b8a1SJoseph Chan static hda_nid_t vt1708_adc_nids[2] = { 400c577b8a1SJoseph Chan /* ADC1-2 */ 401c577b8a1SJoseph Chan 0x15, 0x27 402c577b8a1SJoseph Chan }; 403c577b8a1SJoseph Chan 404c577b8a1SJoseph Chan static hda_nid_t vt1709_adc_nids[3] = { 405c577b8a1SJoseph Chan /* ADC1-2 */ 406c577b8a1SJoseph Chan 0x14, 0x15, 0x16 407c577b8a1SJoseph Chan }; 408c577b8a1SJoseph Chan 409f7278fd0SJosepch Chan static hda_nid_t vt1708B_adc_nids[2] = { 410f7278fd0SJosepch Chan /* ADC1-2 */ 411f7278fd0SJosepch Chan 0x13, 0x14 412f7278fd0SJosepch Chan }; 413f7278fd0SJosepch Chan 414d949cac1SHarald Welte static hda_nid_t vt1708S_adc_nids[2] = { 415d949cac1SHarald Welte /* ADC1-2 */ 416d949cac1SHarald Welte 0x13, 0x14 417d949cac1SHarald Welte }; 418d949cac1SHarald Welte 419d949cac1SHarald Welte static hda_nid_t vt1702_adc_nids[3] = { 420d949cac1SHarald Welte /* ADC1-2 */ 421d949cac1SHarald Welte 0x12, 0x20, 0x1F 422d949cac1SHarald Welte }; 423d949cac1SHarald Welte 424eb7188caSLydia Wang static hda_nid_t vt1718S_adc_nids[2] = { 425eb7188caSLydia Wang /* ADC1-2 */ 426eb7188caSLydia Wang 0x10, 0x11 427eb7188caSLydia Wang }; 428eb7188caSLydia Wang 429f3db423dSLydia Wang static hda_nid_t vt1716S_adc_nids[2] = { 430f3db423dSLydia Wang /* ADC1-2 */ 431f3db423dSLydia Wang 0x13, 0x14 432f3db423dSLydia Wang }; 433f3db423dSLydia Wang 43425eaba2fSLydia Wang static hda_nid_t vt2002P_adc_nids[2] = { 43525eaba2fSLydia Wang /* ADC1-2 */ 43625eaba2fSLydia Wang 0x10, 0x11 43725eaba2fSLydia Wang }; 43825eaba2fSLydia Wang 439ab6734e7SLydia Wang static hda_nid_t vt1812_adc_nids[2] = { 440ab6734e7SLydia Wang /* ADC1-2 */ 441ab6734e7SLydia Wang 0x10, 0x11 442ab6734e7SLydia Wang }; 443ab6734e7SLydia Wang 444ab6734e7SLydia Wang 445c577b8a1SJoseph Chan /* add dynamic controls */ 4467b315bb4STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name, 4477b315bb4STakashi Iwai int idx, unsigned long val) 448c577b8a1SJoseph Chan { 449c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 450c577b8a1SJoseph Chan 451603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 452603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 453c577b8a1SJoseph Chan if (!knew) 454c577b8a1SJoseph Chan return -ENOMEM; 45571eb7dccSLydia Wang *knew = via_control_templates[type]; 456c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 457c577b8a1SJoseph Chan if (!knew->name) 458c577b8a1SJoseph Chan return -ENOMEM; 4594d02d1b6SJaroslav Kysela if (get_amp_nid_(val)) 4605e26dfd0SJaroslav Kysela knew->subdevice = HDA_SUBDEV_AMP_FLAG; 461c577b8a1SJoseph Chan knew->private_value = val; 462c577b8a1SJoseph Chan return 0; 463c577b8a1SJoseph Chan } 464c577b8a1SJoseph Chan 4657b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \ 4667b315bb4STakashi Iwai __via_add_control(spec, type, name, 0, val) 4677b315bb4STakashi Iwai 4685b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec, 4695b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *tmpl) 4705b0cb1d8SJaroslav Kysela { 4715b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 4725b0cb1d8SJaroslav Kysela 4735b0cb1d8SJaroslav Kysela snd_array_init(&spec->kctls, sizeof(*knew), 32); 4745b0cb1d8SJaroslav Kysela knew = snd_array_new(&spec->kctls); 4755b0cb1d8SJaroslav Kysela if (!knew) 4765b0cb1d8SJaroslav Kysela return NULL; 4775b0cb1d8SJaroslav Kysela *knew = *tmpl; 4785b0cb1d8SJaroslav Kysela knew->name = kstrdup(tmpl->name, GFP_KERNEL); 4795b0cb1d8SJaroslav Kysela if (!knew->name) 4805b0cb1d8SJaroslav Kysela return NULL; 481b331439dSTakashi Iwai return knew; 4825b0cb1d8SJaroslav Kysela } 4835b0cb1d8SJaroslav Kysela 484603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 485603c4019STakashi Iwai { 486603c4019STakashi Iwai struct via_spec *spec = codec->spec; 487603c4019STakashi Iwai 488603c4019STakashi Iwai if (spec->kctls.list) { 489603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 490603c4019STakashi Iwai int i; 491603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 492603c4019STakashi Iwai kfree(kctl[i].name); 493603c4019STakashi Iwai } 494603c4019STakashi Iwai snd_array_free(&spec->kctls); 495603c4019STakashi Iwai } 496603c4019STakashi Iwai 497c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 4989510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 4997b315bb4STakashi Iwai int type_idx, int idx, int mix_nid) 500c577b8a1SJoseph Chan { 501c577b8a1SJoseph Chan char name[32]; 502c577b8a1SJoseph Chan int err; 503c577b8a1SJoseph Chan 504c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 5057b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, 506c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 507c577b8a1SJoseph Chan if (err < 0) 508c577b8a1SJoseph Chan return err; 509c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 5107b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, 511c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 512c577b8a1SJoseph Chan if (err < 0) 513c577b8a1SJoseph Chan return err; 514c577b8a1SJoseph Chan return 0; 515c577b8a1SJoseph Chan } 516c577b8a1SJoseph Chan 517c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec, 518c577b8a1SJoseph Chan hda_nid_t nid, int pin_type, 519c577b8a1SJoseph Chan int dac_idx) 520c577b8a1SJoseph Chan { 521c577b8a1SJoseph Chan /* set as output */ 522c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 523c577b8a1SJoseph Chan pin_type); 524c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 525c577b8a1SJoseph Chan AMP_OUT_UNMUTE); 526d3a11e60STakashi Iwai if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) 527d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 528d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 529c577b8a1SJoseph Chan } 530c577b8a1SJoseph Chan 531c577b8a1SJoseph Chan 532c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 533c577b8a1SJoseph Chan { 534c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 535c577b8a1SJoseph Chan int i; 536c577b8a1SJoseph Chan 537c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 538c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.line_out_pins[i]; 539c577b8a1SJoseph Chan if (nid) 540c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); 541c577b8a1SJoseph Chan } 542c577b8a1SJoseph Chan } 543c577b8a1SJoseph Chan 544c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 545c577b8a1SJoseph Chan { 546c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 547c577b8a1SJoseph Chan hda_nid_t pin; 54825eaba2fSLydia Wang int i; 549c577b8a1SJoseph Chan 55025eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 55125eaba2fSLydia Wang pin = spec->autocfg.hp_pins[i]; 552c577b8a1SJoseph Chan if (pin) /* connect to front */ 553c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); 554c577b8a1SJoseph Chan } 55525eaba2fSLydia Wang } 556c577b8a1SJoseph Chan 55732e0191dSClemens Ladisch static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); 55832e0191dSClemens Ladisch 559c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 560c577b8a1SJoseph Chan { 561c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 5627b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 56332e0191dSClemens Ladisch unsigned int ctl; 564c577b8a1SJoseph Chan int i; 565c577b8a1SJoseph Chan 5667b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 5677b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 56832e0191dSClemens Ladisch if (spec->smart51_enabled && is_smart51_pins(spec, nid)) 56932e0191dSClemens Ladisch ctl = PIN_OUT; 57030649676SDavid Henningsson else if (cfg->inputs[i].type == AUTO_PIN_MIC) 57132e0191dSClemens Ladisch ctl = PIN_VREF50; 57232e0191dSClemens Ladisch else 57332e0191dSClemens Ladisch ctl = PIN_IN; 574c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 57532e0191dSClemens Ladisch AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); 576c577b8a1SJoseph Chan } 577c577b8a1SJoseph Chan } 578f5271101SLydia Wang 579f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 580f5271101SLydia Wang unsigned int *affected_parm) 581f5271101SLydia Wang { 582f5271101SLydia Wang unsigned parm; 583f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 584f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 585f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 586f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 587d56757abSTakashi Iwai unsigned present = snd_hda_jack_detect(codec, nid); 5881564b287SLydia Wang struct via_spec *spec = codec->spec; 5891564b287SLydia Wang if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) 5901564b287SLydia Wang || ((no_presence || present) 5911564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 592f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 593f5271101SLydia Wang parm = AC_PWRST_D0; 594f5271101SLydia Wang } else 595f5271101SLydia Wang parm = AC_PWRST_D3; 596f5271101SLydia Wang 597f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 598f5271101SLydia Wang } 599f5271101SLydia Wang 600f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec) 601f5271101SLydia Wang { 602f5271101SLydia Wang struct via_spec *spec = codec->spec; 603f5271101SLydia Wang int imux_is_smixer; 604f5271101SLydia Wang unsigned int parm; 605f5271101SLydia Wang 606f5271101SLydia Wang if (spec->codec_type == VT1702) { 607f5271101SLydia Wang imux_is_smixer = snd_hda_codec_read( 608f5271101SLydia Wang codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 609f5271101SLydia Wang /* inputs */ 610f5271101SLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 611f5271101SLydia Wang parm = AC_PWRST_D3; 612f5271101SLydia Wang set_pin_power_state(codec, 0x14, &parm); 613f5271101SLydia Wang set_pin_power_state(codec, 0x15, &parm); 614f5271101SLydia Wang set_pin_power_state(codec, 0x18, &parm); 615f5271101SLydia Wang if (imux_is_smixer) 616f5271101SLydia Wang parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */ 617f5271101SLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 618f5271101SLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, 619f5271101SLydia Wang parm); 620f5271101SLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, 621f5271101SLydia Wang parm); 622f5271101SLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, 623f5271101SLydia Wang parm); 624f5271101SLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, 625f5271101SLydia Wang parm); 626f5271101SLydia Wang 627f5271101SLydia Wang /* outputs */ 628f5271101SLydia Wang /* PW 3/4 (16h/17h) */ 629f5271101SLydia Wang parm = AC_PWRST_D3; 630f5271101SLydia Wang set_pin_power_state(codec, 0x16, &parm); 631f5271101SLydia Wang set_pin_power_state(codec, 0x17, &parm); 632f5271101SLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 633f5271101SLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 634f5271101SLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 635f5271101SLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 636f5271101SLydia Wang parm); 637f5271101SLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, 638f5271101SLydia Wang parm); 639f5271101SLydia Wang } else if (spec->codec_type == VT1708B_8CH 640f5271101SLydia Wang || spec->codec_type == VT1708B_4CH 641f5271101SLydia Wang || spec->codec_type == VT1708S) { 642f5271101SLydia Wang /* SW0 (17h) = stereo mixer */ 643f5271101SLydia Wang int is_8ch = spec->codec_type != VT1708B_4CH; 644f5271101SLydia Wang imux_is_smixer = snd_hda_codec_read( 645f5271101SLydia Wang codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 646f5271101SLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0); 647f5271101SLydia Wang /* inputs */ 648f5271101SLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 649f5271101SLydia Wang parm = AC_PWRST_D3; 650f5271101SLydia Wang set_pin_power_state(codec, 0x1a, &parm); 651f5271101SLydia Wang set_pin_power_state(codec, 0x1b, &parm); 652f5271101SLydia Wang set_pin_power_state(codec, 0x1e, &parm); 653f5271101SLydia Wang if (imux_is_smixer) 654f5271101SLydia Wang parm = AC_PWRST_D0; 655f5271101SLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 656f5271101SLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, 657f5271101SLydia Wang parm); 658f5271101SLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, 659f5271101SLydia Wang parm); 660f5271101SLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, 661f5271101SLydia Wang parm); 662f5271101SLydia Wang 663f5271101SLydia Wang /* outputs */ 664f5271101SLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 665f5271101SLydia Wang parm = AC_PWRST_D3; 666f5271101SLydia Wang set_pin_power_state(codec, 0x19, &parm); 66732e0191dSClemens Ladisch if (spec->smart51_enabled) 66832e0191dSClemens Ladisch parm = AC_PWRST_D0; 669f5271101SLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, 670f5271101SLydia Wang parm); 671f5271101SLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, 672f5271101SLydia Wang parm); 673f5271101SLydia Wang 674f5271101SLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 675f5271101SLydia Wang if (is_8ch) { 676f5271101SLydia Wang parm = AC_PWRST_D3; 677f5271101SLydia Wang set_pin_power_state(codec, 0x22, &parm); 67832e0191dSClemens Ladisch if (spec->smart51_enabled) 67932e0191dSClemens Ladisch parm = AC_PWRST_D0; 680f5271101SLydia Wang snd_hda_codec_write(codec, 0x26, 0, 681f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 682f5271101SLydia Wang snd_hda_codec_write(codec, 0x24, 0, 683f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 684f5271101SLydia Wang } 685f5271101SLydia Wang 686f5271101SLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 687f5271101SLydia Wang parm = AC_PWRST_D3; 688f5271101SLydia Wang /* force to D0 for internal Speaker */ 689f5271101SLydia Wang set_pin_power_state(codec, 0x1c, &parm); 690f5271101SLydia Wang set_pin_power_state(codec, 0x1d, &parm); 691f5271101SLydia Wang if (is_8ch) 692f5271101SLydia Wang set_pin_power_state(codec, 0x23, &parm); 693f5271101SLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 694f5271101SLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 695f5271101SLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 696f5271101SLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 697f5271101SLydia Wang parm); 698f5271101SLydia Wang if (is_8ch) { 699f5271101SLydia Wang snd_hda_codec_write(codec, 0x25, 0, 700f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 701f5271101SLydia Wang snd_hda_codec_write(codec, 0x27, 0, 702f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 703f5271101SLydia Wang } 704eb7188caSLydia Wang } else if (spec->codec_type == VT1718S) { 705eb7188caSLydia Wang /* MUX6 (1eh) = stereo mixer */ 706eb7188caSLydia Wang imux_is_smixer = snd_hda_codec_read( 707eb7188caSLydia Wang codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 708eb7188caSLydia Wang /* inputs */ 709eb7188caSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 710eb7188caSLydia Wang parm = AC_PWRST_D3; 711eb7188caSLydia Wang set_pin_power_state(codec, 0x29, &parm); 712eb7188caSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 713eb7188caSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 714eb7188caSLydia Wang if (imux_is_smixer) 715eb7188caSLydia Wang parm = AC_PWRST_D0; 716eb7188caSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 717eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, 718eb7188caSLydia Wang parm); 719eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, 720eb7188caSLydia Wang parm); 721eb7188caSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 722eb7188caSLydia Wang parm); 723eb7188caSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, 724eb7188caSLydia Wang parm); 725eb7188caSLydia Wang 726eb7188caSLydia Wang /* outputs */ 727eb7188caSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 728eb7188caSLydia Wang parm = AC_PWRST_D3; 729eb7188caSLydia Wang set_pin_power_state(codec, 0x27, &parm); 730eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 731eb7188caSLydia Wang parm); 732eb7188caSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, 733eb7188caSLydia Wang parm); 734eb7188caSLydia Wang 735eb7188caSLydia Wang /* PW2 (26h), AOW2 (ah) */ 736eb7188caSLydia Wang parm = AC_PWRST_D3; 737eb7188caSLydia Wang set_pin_power_state(codec, 0x26, &parm); 738eb7188caSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, 739eb7188caSLydia Wang parm); 740eb7188caSLydia Wang 741eb7188caSLydia Wang /* PW0/1 (24h/25h) */ 742eb7188caSLydia Wang parm = AC_PWRST_D3; 743eb7188caSLydia Wang set_pin_power_state(codec, 0x24, &parm); 744eb7188caSLydia Wang set_pin_power_state(codec, 0x25, &parm); 745eb7188caSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 746eb7188caSLydia Wang set_pin_power_state(codec, 0x28, &parm); 747eb7188caSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, 748eb7188caSLydia Wang parm); 749eb7188caSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, 750eb7188caSLydia Wang parm); 751eb7188caSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 752eb7188caSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 753eb7188caSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 754eb7188caSLydia Wang if (spec->hp_independent_mode) { 755eb7188caSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 756eb7188caSLydia Wang parm = AC_PWRST_D3; 757eb7188caSLydia Wang set_pin_power_state(codec, 0x28, &parm); 758eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 759eb7188caSLydia Wang AC_VERB_SET_POWER_STATE, parm); 760eb7188caSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 761eb7188caSLydia Wang AC_VERB_SET_POWER_STATE, parm); 762eb7188caSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 763eb7188caSLydia Wang AC_VERB_SET_POWER_STATE, parm); 764eb7188caSLydia Wang } 765f3db423dSLydia Wang } else if (spec->codec_type == VT1716S) { 766f3db423dSLydia Wang unsigned int mono_out, present; 767f3db423dSLydia Wang /* SW0 (17h) = stereo mixer */ 768f3db423dSLydia Wang imux_is_smixer = snd_hda_codec_read( 769f3db423dSLydia Wang codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 770f3db423dSLydia Wang /* inputs */ 771f3db423dSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 772f3db423dSLydia Wang parm = AC_PWRST_D3; 773f3db423dSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 774f3db423dSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 775f3db423dSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 776f3db423dSLydia Wang if (imux_is_smixer) 777f3db423dSLydia Wang parm = AC_PWRST_D0; 778f3db423dSLydia Wang /* SW0 (17h), AIW0(13h) */ 779f3db423dSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, 780f3db423dSLydia Wang parm); 781f3db423dSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, 782f3db423dSLydia Wang parm); 783f3db423dSLydia Wang 784f3db423dSLydia Wang parm = AC_PWRST_D3; 785f3db423dSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 786f3db423dSLydia Wang /* PW11 (22h) */ 787f3db423dSLydia Wang if (spec->dmic_enabled) 788f3db423dSLydia Wang set_pin_power_state(codec, 0x22, &parm); 789f3db423dSLydia Wang else 790f3db423dSLydia Wang snd_hda_codec_write( 791f3db423dSLydia Wang codec, 0x22, 0, 792f3db423dSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 793f3db423dSLydia Wang 794f3db423dSLydia Wang /* SW2(26h), AIW1(14h) */ 795f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, 796f3db423dSLydia Wang parm); 797f3db423dSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, 798f3db423dSLydia Wang parm); 799f3db423dSLydia Wang 800f3db423dSLydia Wang /* outputs */ 801f3db423dSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 802f3db423dSLydia Wang parm = AC_PWRST_D3; 803f3db423dSLydia Wang set_pin_power_state(codec, 0x19, &parm); 804f3db423dSLydia Wang /* Smart 5.1 PW2(1bh) */ 805f3db423dSLydia Wang if (spec->smart51_enabled) 806f3db423dSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 807f3db423dSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, 808f3db423dSLydia Wang parm); 809f3db423dSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, 810f3db423dSLydia Wang parm); 811f3db423dSLydia Wang 812f3db423dSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 813f3db423dSLydia Wang parm = AC_PWRST_D3; 814f3db423dSLydia Wang set_pin_power_state(codec, 0x23, &parm); 815f3db423dSLydia Wang /* Smart 5.1 PW1(1ah) */ 816f3db423dSLydia Wang if (spec->smart51_enabled) 817f3db423dSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 818f3db423dSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, 819f3db423dSLydia Wang parm); 820f3db423dSLydia Wang 821f3db423dSLydia Wang /* Smart 5.1 PW5(1eh) */ 822f3db423dSLydia Wang if (spec->smart51_enabled) 823f3db423dSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 824f3db423dSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, 825f3db423dSLydia Wang parm); 826f3db423dSLydia Wang 827f3db423dSLydia Wang /* Mono out */ 828f3db423dSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 829d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, 0x1c); 830f3db423dSLydia Wang if (present) 831f3db423dSLydia Wang mono_out = 0; 832f3db423dSLydia Wang else { 833d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, 0x1d); 834f3db423dSLydia Wang if (!spec->hp_independent_mode && present) 835f3db423dSLydia Wang mono_out = 0; 836f3db423dSLydia Wang else 837f3db423dSLydia Wang mono_out = 1; 838f3db423dSLydia Wang } 839f3db423dSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 840f3db423dSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, 841f3db423dSLydia Wang parm); 842f3db423dSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, 843f3db423dSLydia Wang parm); 844f3db423dSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, 845f3db423dSLydia Wang parm); 846f3db423dSLydia Wang 847f3db423dSLydia Wang /* PW 3/4 (1ch/1dh) */ 848f3db423dSLydia Wang parm = AC_PWRST_D3; 849f3db423dSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 850f3db423dSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 851f3db423dSLydia Wang /* HP Independent Mode, power on AOW3 */ 852f3db423dSLydia Wang if (spec->hp_independent_mode) 853f3db423dSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 854f3db423dSLydia Wang AC_VERB_SET_POWER_STATE, parm); 855f3db423dSLydia Wang 856f3db423dSLydia Wang /* force to D0 for internal Speaker */ 857f3db423dSLydia Wang /* MW0 (16h), AOW0 (10h) */ 858f3db423dSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 859f3db423dSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 860f3db423dSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 861f3db423dSLydia Wang mono_out ? AC_PWRST_D0 : parm); 86225eaba2fSLydia Wang } else if (spec->codec_type == VT2002P) { 86325eaba2fSLydia Wang unsigned int present; 86425eaba2fSLydia Wang /* MUX9 (1eh) = stereo mixer */ 86525eaba2fSLydia Wang imux_is_smixer = snd_hda_codec_read( 86625eaba2fSLydia Wang codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 86725eaba2fSLydia Wang /* inputs */ 86825eaba2fSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 86925eaba2fSLydia Wang parm = AC_PWRST_D3; 87025eaba2fSLydia Wang set_pin_power_state(codec, 0x29, &parm); 87125eaba2fSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 87225eaba2fSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 87325eaba2fSLydia Wang if (imux_is_smixer) 87425eaba2fSLydia Wang parm = AC_PWRST_D0; 87525eaba2fSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 87625eaba2fSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, 87725eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 87825eaba2fSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, 87925eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 88025eaba2fSLydia Wang snd_hda_codec_write(codec, 0x10, 0, 88125eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 88225eaba2fSLydia Wang snd_hda_codec_write(codec, 0x11, 0, 88325eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 88425eaba2fSLydia Wang 88525eaba2fSLydia Wang /* outputs */ 88625eaba2fSLydia Wang /* AOW0 (8h)*/ 88725eaba2fSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 88825eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 88925eaba2fSLydia Wang 89025eaba2fSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 89125eaba2fSLydia Wang parm = AC_PWRST_D3; 89225eaba2fSLydia Wang set_pin_power_state(codec, 0x26, &parm); 89325eaba2fSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 89425eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 89525eaba2fSLydia Wang snd_hda_codec_write(codec, 0x37, 89625eaba2fSLydia Wang 0, AC_VERB_SET_POWER_STATE, parm); 89725eaba2fSLydia Wang 89825eaba2fSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 89925eaba2fSLydia Wang parm = AC_PWRST_D3; 90025eaba2fSLydia Wang set_pin_power_state(codec, 0x25, &parm); 90125eaba2fSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 90225eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 90325eaba2fSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 90425eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 90525eaba2fSLydia Wang if (spec->hp_independent_mode) { 90625eaba2fSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 90725eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 90825eaba2fSLydia Wang } 90925eaba2fSLydia Wang 91025eaba2fSLydia Wang /* Class-D */ 91125eaba2fSLydia Wang /* PW0 (24h), MW0(18h), MUX0(34h) */ 912d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, 0x25); 91325eaba2fSLydia Wang parm = AC_PWRST_D3; 91425eaba2fSLydia Wang set_pin_power_state(codec, 0x24, &parm); 91525eaba2fSLydia Wang if (present) { 91625eaba2fSLydia Wang snd_hda_codec_write( 91725eaba2fSLydia Wang codec, 0x18, 0, 91825eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 91925eaba2fSLydia Wang snd_hda_codec_write( 92025eaba2fSLydia Wang codec, 0x34, 0, 92125eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 92225eaba2fSLydia Wang } else { 92325eaba2fSLydia Wang snd_hda_codec_write( 92425eaba2fSLydia Wang codec, 0x18, 0, 92525eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 92625eaba2fSLydia Wang snd_hda_codec_write( 92725eaba2fSLydia Wang codec, 0x34, 0, 92825eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 92925eaba2fSLydia Wang } 93025eaba2fSLydia Wang 93125eaba2fSLydia Wang /* Mono Out */ 93225eaba2fSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 933d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, 0x26); 93425eaba2fSLydia Wang parm = AC_PWRST_D3; 93525eaba2fSLydia Wang set_pin_power_state(codec, 0x31, &parm); 93625eaba2fSLydia Wang if (present) { 93725eaba2fSLydia Wang snd_hda_codec_write( 93825eaba2fSLydia Wang codec, 0x17, 0, 93925eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 94025eaba2fSLydia Wang snd_hda_codec_write( 94125eaba2fSLydia Wang codec, 0x3b, 0, 94225eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 94325eaba2fSLydia Wang } else { 94425eaba2fSLydia Wang snd_hda_codec_write( 94525eaba2fSLydia Wang codec, 0x17, 0, 94625eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 94725eaba2fSLydia Wang snd_hda_codec_write( 94825eaba2fSLydia Wang codec, 0x3b, 0, 94925eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 95025eaba2fSLydia Wang } 95125eaba2fSLydia Wang 95225eaba2fSLydia Wang /* MW9 (21h) */ 95325eaba2fSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 95425eaba2fSLydia Wang snd_hda_codec_write( 95525eaba2fSLydia Wang codec, 0x21, 0, 95625eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 95725eaba2fSLydia Wang else 95825eaba2fSLydia Wang snd_hda_codec_write( 95925eaba2fSLydia Wang codec, 0x21, 0, 96025eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 961ab6734e7SLydia Wang } else if (spec->codec_type == VT1812) { 962ab6734e7SLydia Wang unsigned int present; 963ab6734e7SLydia Wang /* MUX10 (1eh) = stereo mixer */ 964ab6734e7SLydia Wang imux_is_smixer = snd_hda_codec_read( 965ab6734e7SLydia Wang codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 966ab6734e7SLydia Wang /* inputs */ 967ab6734e7SLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 968ab6734e7SLydia Wang parm = AC_PWRST_D3; 969ab6734e7SLydia Wang set_pin_power_state(codec, 0x29, &parm); 970ab6734e7SLydia Wang set_pin_power_state(codec, 0x2a, &parm); 971ab6734e7SLydia Wang set_pin_power_state(codec, 0x2b, &parm); 972ab6734e7SLydia Wang if (imux_is_smixer) 973ab6734e7SLydia Wang parm = AC_PWRST_D0; 974ab6734e7SLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 975ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x1e, 0, 976ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 977ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x1f, 0, 978ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 979ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x10, 0, 980ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 981ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x11, 0, 982ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 983ab6734e7SLydia Wang 984ab6734e7SLydia Wang /* outputs */ 985ab6734e7SLydia Wang /* AOW0 (8h)*/ 986ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x8, 0, 987ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 988ab6734e7SLydia Wang 989ab6734e7SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 990ab6734e7SLydia Wang parm = AC_PWRST_D3; 991ab6734e7SLydia Wang set_pin_power_state(codec, 0x28, &parm); 992ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x18, 0, 993ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 994ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x38, 0, 995ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 996ab6734e7SLydia Wang 997ab6734e7SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 998ab6734e7SLydia Wang parm = AC_PWRST_D3; 999ab6734e7SLydia Wang set_pin_power_state(codec, 0x25, &parm); 1000ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x15, 0, 1001ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 1002ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x35, 0, 1003ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 1004ab6734e7SLydia Wang if (spec->hp_independent_mode) { 1005ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x9, 0, 1006ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 1007ab6734e7SLydia Wang } 1008ab6734e7SLydia Wang 1009ab6734e7SLydia Wang /* Internal Speaker */ 1010ab6734e7SLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 1011d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, 0x25); 1012ab6734e7SLydia Wang parm = AC_PWRST_D3; 1013ab6734e7SLydia Wang set_pin_power_state(codec, 0x24, &parm); 1014ab6734e7SLydia Wang if (present) { 1015ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 1016ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1017ab6734e7SLydia Wang AC_PWRST_D3); 1018ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x34, 0, 1019ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1020ab6734e7SLydia Wang AC_PWRST_D3); 1021ab6734e7SLydia Wang } else { 1022ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 1023ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1024ab6734e7SLydia Wang AC_PWRST_D0); 1025ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x34, 0, 1026ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1027ab6734e7SLydia Wang AC_PWRST_D0); 1028ab6734e7SLydia Wang } 1029ab6734e7SLydia Wang /* Mono Out */ 1030ab6734e7SLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 1031d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, 0x28); 1032ab6734e7SLydia Wang parm = AC_PWRST_D3; 1033ab6734e7SLydia Wang set_pin_power_state(codec, 0x31, &parm); 1034ab6734e7SLydia Wang if (present) { 1035ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 1036ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1037ab6734e7SLydia Wang AC_PWRST_D3); 1038ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 1039ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1040ab6734e7SLydia Wang AC_PWRST_D3); 1041ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 1042ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1043ab6734e7SLydia Wang AC_PWRST_D3); 1044ab6734e7SLydia Wang } else { 1045ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 1046ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1047ab6734e7SLydia Wang AC_PWRST_D0); 1048ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 1049ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1050ab6734e7SLydia Wang AC_PWRST_D0); 1051ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 1052ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1053ab6734e7SLydia Wang AC_PWRST_D0); 1054ab6734e7SLydia Wang } 1055ab6734e7SLydia Wang 1056ab6734e7SLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 1057ab6734e7SLydia Wang parm = AC_PWRST_D3; 1058ab6734e7SLydia Wang set_pin_power_state(codec, 0x33, &parm); 1059ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x1d, 0, 1060ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 1061ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x3d, 0, 1062ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 1063ab6734e7SLydia Wang 1064ab6734e7SLydia Wang /* MW9 (21h) */ 1065ab6734e7SLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 1066ab6734e7SLydia Wang snd_hda_codec_write( 1067ab6734e7SLydia Wang codec, 0x21, 0, 1068ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 1069ab6734e7SLydia Wang else 1070ab6734e7SLydia Wang snd_hda_codec_write( 1071ab6734e7SLydia Wang codec, 0x21, 0, 1072ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 1073f5271101SLydia Wang } 1074f5271101SLydia Wang } 1075f5271101SLydia Wang 1076c577b8a1SJoseph Chan /* 1077c577b8a1SJoseph Chan * input MUX handling 1078c577b8a1SJoseph Chan */ 1079c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 1080c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 1081c577b8a1SJoseph Chan { 1082c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1083c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1084c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 1085c577b8a1SJoseph Chan } 1086c577b8a1SJoseph Chan 1087c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 1088c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 1089c577b8a1SJoseph Chan { 1090c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1091c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1092c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 1093c577b8a1SJoseph Chan 1094c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 1095c577b8a1SJoseph Chan return 0; 1096c577b8a1SJoseph Chan } 1097c577b8a1SJoseph Chan 1098c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 1099c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 1100c577b8a1SJoseph Chan { 1101c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1102c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1103c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 1104bff5fbf5SLydia Wang int ret; 1105c577b8a1SJoseph Chan 1106337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 1107337b9d02STakashi Iwai return -EINVAL; 1108a80e6e3cSLydia Wang /* switch to D0 beofre change index */ 1109a80e6e3cSLydia Wang if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, 1110a80e6e3cSLydia Wang AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 1111a80e6e3cSLydia Wang snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 1112a80e6e3cSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 1113bff5fbf5SLydia Wang 1114bff5fbf5SLydia Wang ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 1115bff5fbf5SLydia Wang spec->mux_nids[adc_idx], 1116bff5fbf5SLydia Wang &spec->cur_mux[adc_idx]); 1117a80e6e3cSLydia Wang /* update jack power state */ 1118a80e6e3cSLydia Wang set_jack_power_state(codec); 1119a80e6e3cSLydia Wang 1120bff5fbf5SLydia Wang return ret; 1121c577b8a1SJoseph Chan } 1122c577b8a1SJoseph Chan 11230aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 11240aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 11250aa62aefSHarald Welte { 11260aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 11270aa62aefSHarald Welte struct via_spec *spec = codec->spec; 11280aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 11290aa62aefSHarald Welte } 11300aa62aefSHarald Welte 11310aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 11320aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 11330aa62aefSHarald Welte { 11340aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 11355b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 1136eb7188caSLydia Wang unsigned int pinsel; 1137eb7188caSLydia Wang 1138eb7188caSLydia Wang /* use !! to translate conn sel 2 for VT1718S */ 1139eb7188caSLydia Wang pinsel = !!snd_hda_codec_read(codec, nid, 0, 11400aa62aefSHarald Welte AC_VERB_GET_CONNECT_SEL, 11410aa62aefSHarald Welte 0x00); 11420aa62aefSHarald Welte ucontrol->value.enumerated.item[0] = pinsel; 11430aa62aefSHarald Welte 11440aa62aefSHarald Welte return 0; 11450aa62aefSHarald Welte } 11460aa62aefSHarald Welte 11470713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active) 11480713efebSLydia Wang { 11490713efebSLydia Wang struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); 11500713efebSLydia Wang if (ctl) { 11510713efebSLydia Wang ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 11520713efebSLydia Wang ctl->vd[0].access |= active 11530713efebSLydia Wang ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; 11540713efebSLydia Wang snd_ctl_notify(codec->bus->card, 11550713efebSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); 11560713efebSLydia Wang } 11570713efebSLydia Wang } 11580713efebSLydia Wang 11595b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec) 11605b0cb1d8SJaroslav Kysela { 11615b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 11625b0cb1d8SJaroslav Kysela case VT1708: return 0x1b; 11635b0cb1d8SJaroslav Kysela case VT1709_10CH: return 0x29; 11645b0cb1d8SJaroslav Kysela case VT1708B_8CH: /* fall thru */ 11655b0cb1d8SJaroslav Kysela case VT1708S: return 0x27; 11665b0cb1d8SJaroslav Kysela default: return 0; 11675b0cb1d8SJaroslav Kysela } 11685b0cb1d8SJaroslav Kysela } 11695b0cb1d8SJaroslav Kysela 1170cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec) 1171cdc1784dSLydia Wang { 1172cdc1784dSLydia Wang /* mute side channel */ 1173cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 1174cdc1784dSLydia Wang unsigned int parm = spec->hp_independent_mode 1175cdc1784dSLydia Wang ? AMP_OUT_MUTE : AMP_OUT_UNMUTE; 11765b0cb1d8SJaroslav Kysela hda_nid_t sw3 = side_mute_channel(spec); 1177cdc1784dSLydia Wang 1178cdc1784dSLydia Wang if (sw3) 1179cdc1784dSLydia Wang snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE, 1180cdc1784dSLydia Wang parm); 1181cdc1784dSLydia Wang return 0; 1182cdc1784dSLydia Wang } 1183cdc1784dSLydia Wang 11840aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 11850aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 11860aa62aefSHarald Welte { 11870aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 11880aa62aefSHarald Welte struct via_spec *spec = codec->spec; 11895b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 11900aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 1191cdc1784dSLydia Wang /* Get Independent Mode index of headphone pin widget */ 1192cdc1784dSLydia Wang spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel 1193cdc1784dSLydia Wang ? 1 : 0; 1194*ce0e5a9eSLydia Wang if (spec->codec_type == VT1718S) 1195*ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 1196*ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0); 1197*ce0e5a9eSLydia Wang else 1198*ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 1199*ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 12000aa62aefSHarald Welte 1201*ce0e5a9eSLydia Wang if (spec->codec_type == VT1812) 1202*ce0e5a9eSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 1203*ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 1204cdc1784dSLydia Wang if (spec->multiout.hp_nid && spec->multiout.hp_nid 1205cdc1784dSLydia Wang != spec->multiout.dac_nids[HDA_FRONT]) 1206cdc1784dSLydia Wang snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, 12070aa62aefSHarald Welte 0, 0, 0); 12080aa62aefSHarald Welte 1209cdc1784dSLydia Wang update_side_mute_status(codec); 12100713efebSLydia Wang /* update HP volume/swtich active state */ 12110713efebSLydia Wang if (spec->codec_type == VT1708S 1212eb7188caSLydia Wang || spec->codec_type == VT1702 1213f3db423dSLydia Wang || spec->codec_type == VT1718S 121425eaba2fSLydia Wang || spec->codec_type == VT1716S 1215ab6734e7SLydia Wang || spec->codec_type == VT2002P 1216ab6734e7SLydia Wang || spec->codec_type == VT1812) { 12170713efebSLydia Wang activate_ctl(codec, "Headphone Playback Volume", 12180713efebSLydia Wang spec->hp_independent_mode); 12190713efebSLydia Wang activate_ctl(codec, "Headphone Playback Switch", 12200713efebSLydia Wang spec->hp_independent_mode); 12210713efebSLydia Wang } 1222*ce0e5a9eSLydia Wang /* update jack power state */ 1223*ce0e5a9eSLydia Wang set_jack_power_state(codec); 12240aa62aefSHarald Welte return 0; 12250aa62aefSHarald Welte } 12260aa62aefSHarald Welte 12275b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_hp_mixer[2] = { 12280aa62aefSHarald Welte { 12290aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 12300aa62aefSHarald Welte .name = "Independent HP", 12310aa62aefSHarald Welte .info = via_independent_hp_info, 12320aa62aefSHarald Welte .get = via_independent_hp_get, 12330aa62aefSHarald Welte .put = via_independent_hp_put, 12340aa62aefSHarald Welte }, 12355b0cb1d8SJaroslav Kysela { 12365b0cb1d8SJaroslav Kysela .iface = NID_MAPPING, 12375b0cb1d8SJaroslav Kysela .name = "Independent HP", 12385b0cb1d8SJaroslav Kysela }, 12390aa62aefSHarald Welte }; 12400aa62aefSHarald Welte 12413d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec) 12425b0cb1d8SJaroslav Kysela { 12433d83e577STakashi Iwai struct via_spec *spec = codec->spec; 12445b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 12455b0cb1d8SJaroslav Kysela hda_nid_t nid; 12463d83e577STakashi Iwai int nums; 12473d83e577STakashi Iwai hda_nid_t conn[HDA_MAX_CONNECTIONS]; 12485b0cb1d8SJaroslav Kysela 12495b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 12505b0cb1d8SJaroslav Kysela case VT1718S: 12515b0cb1d8SJaroslav Kysela nid = 0x34; 12525b0cb1d8SJaroslav Kysela break; 12535b0cb1d8SJaroslav Kysela case VT2002P: 12545b0cb1d8SJaroslav Kysela nid = 0x35; 12555b0cb1d8SJaroslav Kysela break; 12565b0cb1d8SJaroslav Kysela case VT1812: 12575b0cb1d8SJaroslav Kysela nid = 0x3d; 12585b0cb1d8SJaroslav Kysela break; 12595b0cb1d8SJaroslav Kysela default: 12605b0cb1d8SJaroslav Kysela nid = spec->autocfg.hp_pins[0]; 12615b0cb1d8SJaroslav Kysela break; 12625b0cb1d8SJaroslav Kysela } 12635b0cb1d8SJaroslav Kysela 12643d83e577STakashi Iwai nums = snd_hda_get_connections(codec, nid, conn, HDA_MAX_CONNECTIONS); 12653d83e577STakashi Iwai if (nums <= 1) 12663d83e577STakashi Iwai return 0; 12673d83e577STakashi Iwai 12683d83e577STakashi Iwai knew = via_clone_control(spec, &via_hp_mixer[0]); 12693d83e577STakashi Iwai if (knew == NULL) 12703d83e577STakashi Iwai return -ENOMEM; 12713d83e577STakashi Iwai 12725b0cb1d8SJaroslav Kysela knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 12735b0cb1d8SJaroslav Kysela knew->private_value = nid; 12745b0cb1d8SJaroslav Kysela 12755b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_hp_mixer[1]); 12765b0cb1d8SJaroslav Kysela if (knew == NULL) 12775b0cb1d8SJaroslav Kysela return -ENOMEM; 12785b0cb1d8SJaroslav Kysela knew->subdevice = side_mute_channel(spec); 12795b0cb1d8SJaroslav Kysela 12805b0cb1d8SJaroslav Kysela return 0; 12815b0cb1d8SJaroslav Kysela } 12825b0cb1d8SJaroslav Kysela 12831564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 12841564b287SLydia Wang { 12851564b287SLydia Wang int i; 12861564b287SLydia Wang struct snd_ctl_elem_id id; 12871564b287SLydia Wang const char *labels[] = {"Mic", "Front Mic", "Line"}; 12881564b287SLydia Wang 12891564b287SLydia Wang memset(&id, 0, sizeof(id)); 12901564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 12911564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(labels); i++) { 12921564b287SLydia Wang sprintf(id.name, "%s Playback Volume", labels[i]); 12931564b287SLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 12941564b287SLydia Wang &id); 12951564b287SLydia Wang } 12961564b287SLydia Wang } 12971564b287SLydia Wang 12981564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 12991564b287SLydia Wang { 13001564b287SLydia Wang struct via_spec *spec = codec->spec; 13011564b287SLydia Wang hda_nid_t nid_mixer; 13021564b287SLydia Wang int start_idx; 13031564b287SLydia Wang int end_idx; 13041564b287SLydia Wang int i; 13051564b287SLydia Wang /* get nid of MW0 and start & end index */ 13061564b287SLydia Wang switch (spec->codec_type) { 13071564b287SLydia Wang case VT1708: 13081564b287SLydia Wang nid_mixer = 0x17; 13091564b287SLydia Wang start_idx = 2; 13101564b287SLydia Wang end_idx = 4; 13111564b287SLydia Wang break; 13121564b287SLydia Wang case VT1709_10CH: 13131564b287SLydia Wang case VT1709_6CH: 13141564b287SLydia Wang nid_mixer = 0x18; 13151564b287SLydia Wang start_idx = 2; 13161564b287SLydia Wang end_idx = 4; 13171564b287SLydia Wang break; 13181564b287SLydia Wang case VT1708B_8CH: 13191564b287SLydia Wang case VT1708B_4CH: 13201564b287SLydia Wang case VT1708S: 1321f3db423dSLydia Wang case VT1716S: 13221564b287SLydia Wang nid_mixer = 0x16; 13231564b287SLydia Wang start_idx = 2; 13241564b287SLydia Wang end_idx = 4; 13251564b287SLydia Wang break; 13261564b287SLydia Wang default: 13271564b287SLydia Wang return; 13281564b287SLydia Wang } 13291564b287SLydia Wang /* check AA path's mute status */ 13301564b287SLydia Wang for (i = start_idx; i <= end_idx; i++) { 13311564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 13321564b287SLydia Wang snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i, 13331564b287SLydia Wang HDA_AMP_MUTE, val); 13341564b287SLydia Wang } 13351564b287SLydia Wang } 13361564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) 13371564b287SLydia Wang { 13387b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 13397b315bb4STakashi Iwai int i; 13407b315bb4STakashi Iwai 13417b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 13427b315bb4STakashi Iwai if (pin == cfg->inputs[i].pin) 134386e2959aSTakashi Iwai return cfg->inputs[i].type <= AUTO_PIN_LINE_IN; 13441564b287SLydia Wang } 13457b315bb4STakashi Iwai return 0; 13461564b287SLydia Wang } 13471564b287SLydia Wang 13481564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol, 13491564b287SLydia Wang struct snd_ctl_elem_info *uinfo) 13501564b287SLydia Wang { 13511564b287SLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 13521564b287SLydia Wang uinfo->count = 1; 13531564b287SLydia Wang uinfo->value.integer.min = 0; 13541564b287SLydia Wang uinfo->value.integer.max = 1; 13551564b287SLydia Wang return 0; 13561564b287SLydia Wang } 13571564b287SLydia Wang 13581564b287SLydia Wang static int via_smart51_get(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; 13637b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 13641564b287SLydia Wang int on = 1; 13651564b287SLydia Wang int i; 13661564b287SLydia Wang 13677b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 13687b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 13697b315bb4STakashi Iwai int ctl = snd_hda_codec_read(codec, nid, 0, 13707b315bb4STakashi Iwai AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 137186e2959aSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 13727b315bb4STakashi Iwai continue; 137386e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 13747b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 13751564b287SLydia Wang continue; /* ignore FMic for independent HP */ 13767b315bb4STakashi Iwai if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) 13771564b287SLydia Wang on = 0; 13781564b287SLydia Wang } 13791564b287SLydia Wang *ucontrol->value.integer.value = on; 13801564b287SLydia Wang return 0; 13811564b287SLydia Wang } 13821564b287SLydia Wang 13831564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 13841564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 13851564b287SLydia Wang { 13861564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 13871564b287SLydia Wang struct via_spec *spec = codec->spec; 13887b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 13891564b287SLydia Wang int out_in = *ucontrol->value.integer.value 13901564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 13911564b287SLydia Wang int i; 13921564b287SLydia Wang 13937b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 13947b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 13957b315bb4STakashi Iwai unsigned int parm; 13967b315bb4STakashi Iwai 139786e2959aSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 13987b315bb4STakashi Iwai continue; 139986e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 14007b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 14011564b287SLydia Wang continue; /* don't retask FMic for independent HP */ 14027b315bb4STakashi Iwai 14037b315bb4STakashi Iwai parm = snd_hda_codec_read(codec, nid, 0, 14041564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 14051564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 14061564b287SLydia Wang parm |= out_in; 14071564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 14081564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 14091564b287SLydia Wang parm); 14101564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 14111564b287SLydia Wang mute_aa_path(codec, 1); 14121564b287SLydia Wang notify_aa_path_ctls(codec); 14131564b287SLydia Wang } 14147b315bb4STakashi Iwai if (spec->codec_type == VT1718S) { 1415eb7188caSLydia Wang snd_hda_codec_amp_stereo( 1416eb7188caSLydia Wang codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, 1417eb7188caSLydia Wang HDA_AMP_UNMUTE); 14181564b287SLydia Wang } 141986e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC) { 1420f3db423dSLydia Wang if (spec->codec_type == VT1708S 1421f3db423dSLydia Wang || spec->codec_type == VT1716S) { 14221564b287SLydia Wang /* input = index 1 (AOW3) */ 14231564b287SLydia Wang snd_hda_codec_write( 14241564b287SLydia Wang codec, nid, 0, 14251564b287SLydia Wang AC_VERB_SET_CONNECT_SEL, 1); 14261564b287SLydia Wang snd_hda_codec_amp_stereo( 14271564b287SLydia Wang codec, nid, HDA_OUTPUT, 14281564b287SLydia Wang 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); 14291564b287SLydia Wang } 14301564b287SLydia Wang } 14311564b287SLydia Wang } 14321564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 14331564b287SLydia Wang set_jack_power_state(codec); 14341564b287SLydia Wang return 1; 14351564b287SLydia Wang } 14361564b287SLydia Wang 14375b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_smart51_mixer[2] = { 14381564b287SLydia Wang { 14391564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 14401564b287SLydia Wang .name = "Smart 5.1", 14411564b287SLydia Wang .count = 1, 14421564b287SLydia Wang .info = via_smart51_info, 14431564b287SLydia Wang .get = via_smart51_get, 14441564b287SLydia Wang .put = via_smart51_put, 14451564b287SLydia Wang }, 14465b0cb1d8SJaroslav Kysela { 14475b0cb1d8SJaroslav Kysela .iface = NID_MAPPING, 14485b0cb1d8SJaroslav Kysela .name = "Smart 5.1", 14495b0cb1d8SJaroslav Kysela } 14501564b287SLydia Wang }; 14511564b287SLydia Wang 14525b0cb1d8SJaroslav Kysela static int via_smart51_build(struct via_spec *spec) 14535b0cb1d8SJaroslav Kysela { 14545b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 14557b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 14565b0cb1d8SJaroslav Kysela hda_nid_t nid; 14575b0cb1d8SJaroslav Kysela int i; 14585b0cb1d8SJaroslav Kysela 14595b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_smart51_mixer[0]); 14605b0cb1d8SJaroslav Kysela if (knew == NULL) 14615b0cb1d8SJaroslav Kysela return -ENOMEM; 14625b0cb1d8SJaroslav Kysela 14637b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 14647b315bb4STakashi Iwai nid = cfg->inputs[i].pin; 146586e2959aSTakashi Iwai if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) { 14665b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_smart51_mixer[1]); 14675b0cb1d8SJaroslav Kysela if (knew == NULL) 14685b0cb1d8SJaroslav Kysela return -ENOMEM; 14695b0cb1d8SJaroslav Kysela knew->subdevice = nid; 14707b315bb4STakashi Iwai break; 14715b0cb1d8SJaroslav Kysela } 14725b0cb1d8SJaroslav Kysela } 14735b0cb1d8SJaroslav Kysela 14745b0cb1d8SJaroslav Kysela return 0; 14755b0cb1d8SJaroslav Kysela } 14765b0cb1d8SJaroslav Kysela 1477c577b8a1SJoseph Chan /* capture mixer elements */ 1478c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_capture_mixer[] = { 1479c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), 1480c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), 1481c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), 1482c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), 1483c577b8a1SJoseph Chan { 1484c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1485c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 1486c577b8a1SJoseph Chan * So call somewhat different.. 1487c577b8a1SJoseph Chan */ 1488c577b8a1SJoseph Chan /* .name = "Capture Source", */ 1489c577b8a1SJoseph Chan .name = "Input Source", 1490c577b8a1SJoseph Chan .count = 1, 1491c577b8a1SJoseph Chan .info = via_mux_enum_info, 1492c577b8a1SJoseph Chan .get = via_mux_enum_get, 1493c577b8a1SJoseph Chan .put = via_mux_enum_put, 1494c577b8a1SJoseph Chan }, 1495c577b8a1SJoseph Chan { } /* end */ 1496c577b8a1SJoseph Chan }; 1497f5271101SLydia Wang 1498f5271101SLydia Wang /* check AA path's mute statue */ 1499f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec) 1500f5271101SLydia Wang { 1501f5271101SLydia Wang int mute = 1; 1502f5271101SLydia Wang hda_nid_t nid_mixer; 1503f5271101SLydia Wang int start_idx; 1504f5271101SLydia Wang int end_idx; 1505f5271101SLydia Wang int i; 1506f5271101SLydia Wang struct via_spec *spec = codec->spec; 1507f5271101SLydia Wang /* get nid of MW0 and start & end index */ 1508f5271101SLydia Wang switch (spec->codec_type) { 1509f5271101SLydia Wang case VT1708B_8CH: 1510f5271101SLydia Wang case VT1708B_4CH: 1511f5271101SLydia Wang case VT1708S: 1512f3db423dSLydia Wang case VT1716S: 1513f5271101SLydia Wang nid_mixer = 0x16; 1514f5271101SLydia Wang start_idx = 2; 1515f5271101SLydia Wang end_idx = 4; 1516f5271101SLydia Wang break; 1517f5271101SLydia Wang case VT1702: 1518f5271101SLydia Wang nid_mixer = 0x1a; 1519f5271101SLydia Wang start_idx = 1; 1520f5271101SLydia Wang end_idx = 3; 1521f5271101SLydia Wang break; 1522eb7188caSLydia Wang case VT1718S: 1523eb7188caSLydia Wang nid_mixer = 0x21; 1524eb7188caSLydia Wang start_idx = 1; 1525eb7188caSLydia Wang end_idx = 3; 1526eb7188caSLydia Wang break; 152725eaba2fSLydia Wang case VT2002P: 1528ab6734e7SLydia Wang case VT1812: 152925eaba2fSLydia Wang nid_mixer = 0x21; 153025eaba2fSLydia Wang start_idx = 0; 153125eaba2fSLydia Wang end_idx = 2; 153225eaba2fSLydia Wang break; 1533f5271101SLydia Wang default: 1534f5271101SLydia Wang return 0; 1535f5271101SLydia Wang } 1536f5271101SLydia Wang /* check AA path's mute status */ 1537f5271101SLydia Wang for (i = start_idx; i <= end_idx; i++) { 1538f5271101SLydia Wang unsigned int con_list = snd_hda_codec_read( 1539f5271101SLydia Wang codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); 1540f5271101SLydia Wang int shift = 8 * (i % 4); 1541f5271101SLydia Wang hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; 1542f5271101SLydia Wang unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); 1543f5271101SLydia Wang if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { 1544f5271101SLydia Wang /* check mute status while the pin is connected */ 1545f5271101SLydia Wang int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, 1546f5271101SLydia Wang HDA_INPUT, i) >> 7; 1547f5271101SLydia Wang int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, 1548f5271101SLydia Wang HDA_INPUT, i) >> 7; 1549f5271101SLydia Wang if (!mute_l || !mute_r) { 1550f5271101SLydia Wang mute = 0; 1551f5271101SLydia Wang break; 1552f5271101SLydia Wang } 1553f5271101SLydia Wang } 1554f5271101SLydia Wang } 1555f5271101SLydia Wang return mute; 1556f5271101SLydia Wang } 1557f5271101SLydia Wang 1558f5271101SLydia Wang /* enter/exit analog low-current mode */ 1559f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) 1560f5271101SLydia Wang { 1561f5271101SLydia Wang struct via_spec *spec = codec->spec; 1562f5271101SLydia Wang static int saved_stream_idle = 1; /* saved stream idle status */ 1563f5271101SLydia Wang int enable = is_aa_path_mute(codec); 1564f5271101SLydia Wang unsigned int verb = 0; 1565f5271101SLydia Wang unsigned int parm = 0; 1566f5271101SLydia Wang 1567f5271101SLydia Wang if (stream_idle == -1) /* stream status did not change */ 1568f5271101SLydia Wang enable = enable && saved_stream_idle; 1569f5271101SLydia Wang else { 1570f5271101SLydia Wang enable = enable && stream_idle; 1571f5271101SLydia Wang saved_stream_idle = stream_idle; 1572f5271101SLydia Wang } 1573f5271101SLydia Wang 1574f5271101SLydia Wang /* decide low current mode's verb & parameter */ 1575f5271101SLydia Wang switch (spec->codec_type) { 1576f5271101SLydia Wang case VT1708B_8CH: 1577f5271101SLydia Wang case VT1708B_4CH: 1578f5271101SLydia Wang verb = 0xf70; 1579f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 1580f5271101SLydia Wang break; 1581f5271101SLydia Wang case VT1708S: 1582eb7188caSLydia Wang case VT1718S: 1583f3db423dSLydia Wang case VT1716S: 1584f5271101SLydia Wang verb = 0xf73; 1585f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 1586f5271101SLydia Wang break; 1587f5271101SLydia Wang case VT1702: 1588f5271101SLydia Wang verb = 0xf73; 1589f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 1590f5271101SLydia Wang break; 159125eaba2fSLydia Wang case VT2002P: 1592ab6734e7SLydia Wang case VT1812: 159325eaba2fSLydia Wang verb = 0xf93; 159425eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 159525eaba2fSLydia Wang break; 1596f5271101SLydia Wang default: 1597f5271101SLydia Wang return; /* other codecs are not supported */ 1598f5271101SLydia Wang } 1599f5271101SLydia Wang /* send verb */ 1600f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 1601f5271101SLydia Wang } 1602f5271101SLydia Wang 1603c577b8a1SJoseph Chan /* 1604c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1605c577b8a1SJoseph Chan */ 1606c577b8a1SJoseph Chan static struct hda_verb vt1708_volume_init_verbs[] = { 1607c577b8a1SJoseph Chan /* 1608c577b8a1SJoseph Chan * Unmute ADC0-1 and set the default input to mic-in 1609c577b8a1SJoseph Chan */ 1610c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1611c577b8a1SJoseph Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1612c577b8a1SJoseph Chan 1613c577b8a1SJoseph Chan 1614f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1615c577b8a1SJoseph Chan * mixer widget 1616c577b8a1SJoseph Chan */ 1617c577b8a1SJoseph Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1618f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1619f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1620f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1621f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1622f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1623c577b8a1SJoseph Chan 1624c577b8a1SJoseph Chan /* 1625c577b8a1SJoseph Chan * Set up output mixers (0x19 - 0x1b) 1626c577b8a1SJoseph Chan */ 1627c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 1628c577b8a1SJoseph Chan {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1629c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1630c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1631c577b8a1SJoseph Chan 1632bfdc675aSLydia Wang /* Setup default input MW0 to PW4 */ 1633bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 1634c577b8a1SJoseph Chan /* PW9 Output enable */ 1635c577b8a1SJoseph Chan {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1636f7278fd0SJosepch Chan { } 1637c577b8a1SJoseph Chan }; 1638c577b8a1SJoseph Chan 1639c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, 1640c577b8a1SJoseph Chan struct hda_codec *codec, 1641c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1642c577b8a1SJoseph Chan { 1643c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 164417314379SLydia Wang int idle = substream->pstr->substream_opened == 1 164517314379SLydia Wang && substream->ref_count == 0; 164617314379SLydia Wang analog_low_current_mode(codec, idle); 16479a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 16489a08160bSTakashi Iwai hinfo); 1649c577b8a1SJoseph Chan } 1650c577b8a1SJoseph Chan 16510aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec, 16520aa62aefSHarald Welte unsigned int stream_tag, 16530aa62aefSHarald Welte unsigned int format, 16540aa62aefSHarald Welte struct snd_pcm_substream *substream) 16550aa62aefSHarald Welte { 16560aa62aefSHarald Welte struct via_spec *spec = codec->spec; 16570aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 16580aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 16590aa62aefSHarald Welte int chs = substream->runtime->channels; 16600aa62aefSHarald Welte int i; 16610aa62aefSHarald Welte 16620aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 16630aa62aefSHarald Welte if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { 16640aa62aefSHarald Welte if (chs == 2 && 16650aa62aefSHarald Welte snd_hda_is_supported_format(codec, mout->dig_out_nid, 16660aa62aefSHarald Welte format) && 16670aa62aefSHarald Welte !(codec->spdif_status & IEC958_AES0_NONAUDIO)) { 16680aa62aefSHarald Welte mout->dig_out_used = HDA_DIG_ANALOG_DUP; 16690aa62aefSHarald Welte /* turn off SPDIF once; otherwise the IEC958 bits won't 16700aa62aefSHarald Welte * be updated */ 16710aa62aefSHarald Welte if (codec->spdif_ctls & AC_DIG1_ENABLE) 16720aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 16730aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 16740aa62aefSHarald Welte codec->spdif_ctls & 16750aa62aefSHarald Welte ~AC_DIG1_ENABLE & 0xff); 16760aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 16770aa62aefSHarald Welte stream_tag, 0, format); 16780aa62aefSHarald Welte /* turn on again (if needed) */ 16790aa62aefSHarald Welte if (codec->spdif_ctls & AC_DIG1_ENABLE) 16800aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 16810aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 16820aa62aefSHarald Welte codec->spdif_ctls & 0xff); 16830aa62aefSHarald Welte } else { 16840aa62aefSHarald Welte mout->dig_out_used = 0; 16850aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 16860aa62aefSHarald Welte 0, 0, 0); 16870aa62aefSHarald Welte } 16880aa62aefSHarald Welte } 16890aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 16900aa62aefSHarald Welte 16910aa62aefSHarald Welte /* front */ 16920aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 16930aa62aefSHarald Welte 0, format); 16940aa62aefSHarald Welte 1695eb7188caSLydia Wang if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] 1696eb7188caSLydia Wang && !spec->hp_independent_mode) 16970aa62aefSHarald Welte /* headphone out will just decode front left/right (stereo) */ 16980aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 16990aa62aefSHarald Welte 0, format); 17000aa62aefSHarald Welte 17010aa62aefSHarald Welte /* extra outputs copied from front */ 17020aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 17030aa62aefSHarald Welte if (mout->extra_out_nid[i]) 17040aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 17050aa62aefSHarald Welte mout->extra_out_nid[i], 17060aa62aefSHarald Welte stream_tag, 0, format); 17070aa62aefSHarald Welte 17080aa62aefSHarald Welte /* surrounds */ 17090aa62aefSHarald Welte for (i = 1; i < mout->num_dacs; i++) { 17100aa62aefSHarald Welte if (chs >= (i + 1) * 2) /* independent out */ 17110aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 17120aa62aefSHarald Welte i * 2, format); 17130aa62aefSHarald Welte else /* copy front */ 17140aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 17150aa62aefSHarald Welte 0, format); 17160aa62aefSHarald Welte } 17170aa62aefSHarald Welte } 17180aa62aefSHarald Welte 17190aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 17200aa62aefSHarald Welte struct hda_codec *codec, 17210aa62aefSHarald Welte unsigned int stream_tag, 17220aa62aefSHarald Welte unsigned int format, 17230aa62aefSHarald Welte struct snd_pcm_substream *substream) 17240aa62aefSHarald Welte { 17250aa62aefSHarald Welte struct via_spec *spec = codec->spec; 17260aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 17270aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 17280aa62aefSHarald Welte 17290aa62aefSHarald Welte if (substream->number == 0) 17300aa62aefSHarald Welte playback_multi_pcm_prep_0(codec, stream_tag, format, 17310aa62aefSHarald Welte substream); 17320aa62aefSHarald Welte else { 17330aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 17340aa62aefSHarald Welte spec->hp_independent_mode) 17350aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 17360aa62aefSHarald Welte stream_tag, 0, format); 17370aa62aefSHarald Welte } 17381f2e99feSLydia Wang vt1708_start_hp_work(spec); 17390aa62aefSHarald Welte return 0; 17400aa62aefSHarald Welte } 17410aa62aefSHarald Welte 17420aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 17430aa62aefSHarald Welte struct hda_codec *codec, 17440aa62aefSHarald Welte struct snd_pcm_substream *substream) 17450aa62aefSHarald Welte { 17460aa62aefSHarald Welte struct via_spec *spec = codec->spec; 17470aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 17480aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 17490aa62aefSHarald Welte int i; 17500aa62aefSHarald Welte 17510aa62aefSHarald Welte if (substream->number == 0) { 17520aa62aefSHarald Welte for (i = 0; i < mout->num_dacs; i++) 17530aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); 17540aa62aefSHarald Welte 17550aa62aefSHarald Welte if (mout->hp_nid && !spec->hp_independent_mode) 17560aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 17570aa62aefSHarald Welte 0, 0, 0); 17580aa62aefSHarald Welte 17590aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 17600aa62aefSHarald Welte if (mout->extra_out_nid[i]) 17610aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 17620aa62aefSHarald Welte mout->extra_out_nid[i], 17630aa62aefSHarald Welte 0, 0, 0); 17640aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 17650aa62aefSHarald Welte if (mout->dig_out_nid && 17660aa62aefSHarald Welte mout->dig_out_used == HDA_DIG_ANALOG_DUP) { 17670aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 17680aa62aefSHarald Welte 0, 0, 0); 17690aa62aefSHarald Welte mout->dig_out_used = 0; 17700aa62aefSHarald Welte } 17710aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 17720aa62aefSHarald Welte } else { 17730aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 17740aa62aefSHarald Welte spec->hp_independent_mode) 17750aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 17760aa62aefSHarald Welte 0, 0, 0); 17770aa62aefSHarald Welte } 17781f2e99feSLydia Wang vt1708_stop_hp_work(spec); 17790aa62aefSHarald Welte return 0; 17800aa62aefSHarald Welte } 17810aa62aefSHarald Welte 1782c577b8a1SJoseph Chan /* 1783c577b8a1SJoseph Chan * Digital out 1784c577b8a1SJoseph Chan */ 1785c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1786c577b8a1SJoseph Chan struct hda_codec *codec, 1787c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1788c577b8a1SJoseph Chan { 1789c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1790c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1791c577b8a1SJoseph Chan } 1792c577b8a1SJoseph Chan 1793c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1794c577b8a1SJoseph Chan struct hda_codec *codec, 1795c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1796c577b8a1SJoseph Chan { 1797c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1798c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1799c577b8a1SJoseph Chan } 1800c577b8a1SJoseph Chan 18015691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 180298aa34c0SHarald Welte struct hda_codec *codec, 180398aa34c0SHarald Welte unsigned int stream_tag, 180498aa34c0SHarald Welte unsigned int format, 180598aa34c0SHarald Welte struct snd_pcm_substream *substream) 180698aa34c0SHarald Welte { 180798aa34c0SHarald Welte struct via_spec *spec = codec->spec; 18089da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 18099da29271STakashi Iwai stream_tag, format, substream); 18109da29271STakashi Iwai } 18115691ec7fSHarald Welte 18129da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 18139da29271STakashi Iwai struct hda_codec *codec, 18149da29271STakashi Iwai struct snd_pcm_substream *substream) 18159da29271STakashi Iwai { 18169da29271STakashi Iwai struct via_spec *spec = codec->spec; 18179da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 181898aa34c0SHarald Welte return 0; 181998aa34c0SHarald Welte } 182098aa34c0SHarald Welte 1821c577b8a1SJoseph Chan /* 1822c577b8a1SJoseph Chan * Analog capture 1823c577b8a1SJoseph Chan */ 1824c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1825c577b8a1SJoseph Chan struct hda_codec *codec, 1826c577b8a1SJoseph Chan unsigned int stream_tag, 1827c577b8a1SJoseph Chan unsigned int format, 1828c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1829c577b8a1SJoseph Chan { 1830c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1831c577b8a1SJoseph Chan 1832c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1833c577b8a1SJoseph Chan stream_tag, 0, format); 1834c577b8a1SJoseph Chan return 0; 1835c577b8a1SJoseph Chan } 1836c577b8a1SJoseph Chan 1837c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1838c577b8a1SJoseph Chan struct hda_codec *codec, 1839c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1840c577b8a1SJoseph Chan { 1841c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1842888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1843c577b8a1SJoseph Chan return 0; 1844c577b8a1SJoseph Chan } 1845c577b8a1SJoseph Chan 1846c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_playback = { 18470aa62aefSHarald Welte .substreams = 2, 1848c577b8a1SJoseph Chan .channels_min = 2, 1849c577b8a1SJoseph Chan .channels_max = 8, 1850c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 1851c577b8a1SJoseph Chan .ops = { 1852c577b8a1SJoseph Chan .open = via_playback_pcm_open, 18530aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 18540aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1855c577b8a1SJoseph Chan }, 1856c577b8a1SJoseph Chan }; 1857c577b8a1SJoseph Chan 1858bc9b5623STakashi Iwai static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 1859c873cc25SLydia Wang .substreams = 2, 1860bc9b5623STakashi Iwai .channels_min = 2, 1861bc9b5623STakashi Iwai .channels_max = 8, 1862bc9b5623STakashi Iwai .nid = 0x10, /* NID to query formats and rates */ 1863bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1864bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1865bc9b5623STakashi Iwai * disable the 24bit format, so far. 1866bc9b5623STakashi Iwai */ 1867bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1868bc9b5623STakashi Iwai .ops = { 1869bc9b5623STakashi Iwai .open = via_playback_pcm_open, 1870c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1871c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1872bc9b5623STakashi Iwai }, 1873bc9b5623STakashi Iwai }; 1874bc9b5623STakashi Iwai 1875c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_capture = { 1876c577b8a1SJoseph Chan .substreams = 2, 1877c577b8a1SJoseph Chan .channels_min = 2, 1878c577b8a1SJoseph Chan .channels_max = 2, 1879c577b8a1SJoseph Chan .nid = 0x15, /* NID to query formats and rates */ 1880c577b8a1SJoseph Chan .ops = { 1881c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1882c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1883c577b8a1SJoseph Chan }, 1884c577b8a1SJoseph Chan }; 1885c577b8a1SJoseph Chan 1886c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_playback = { 1887c577b8a1SJoseph Chan .substreams = 1, 1888c577b8a1SJoseph Chan .channels_min = 2, 1889c577b8a1SJoseph Chan .channels_max = 2, 1890c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1891c577b8a1SJoseph Chan .ops = { 1892c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 18936b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 18949da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 18959da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1896c577b8a1SJoseph Chan }, 1897c577b8a1SJoseph Chan }; 1898c577b8a1SJoseph Chan 1899c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_capture = { 1900c577b8a1SJoseph Chan .substreams = 1, 1901c577b8a1SJoseph Chan .channels_min = 2, 1902c577b8a1SJoseph Chan .channels_max = 2, 1903c577b8a1SJoseph Chan }; 1904c577b8a1SJoseph Chan 1905c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1906c577b8a1SJoseph Chan { 1907c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 19085b0cb1d8SJaroslav Kysela struct snd_kcontrol *kctl; 19095b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 19105b0cb1d8SJaroslav Kysela int err, i; 1911c577b8a1SJoseph Chan 1912c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1913c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1914c577b8a1SJoseph Chan if (err < 0) 1915c577b8a1SJoseph Chan return err; 1916c577b8a1SJoseph Chan } 1917c577b8a1SJoseph Chan 1918c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1919c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 1920c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1921c577b8a1SJoseph Chan if (err < 0) 1922c577b8a1SJoseph Chan return err; 19239a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 19249a08160bSTakashi Iwai &spec->multiout); 19259a08160bSTakashi Iwai if (err < 0) 19269a08160bSTakashi Iwai return err; 19279a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1928c577b8a1SJoseph Chan } 1929c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1930c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1931c577b8a1SJoseph Chan if (err < 0) 1932c577b8a1SJoseph Chan return err; 1933c577b8a1SJoseph Chan } 193417314379SLydia Wang 19355b0cb1d8SJaroslav Kysela /* assign Capture Source enums to NID */ 19365b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); 19375b0cb1d8SJaroslav Kysela for (i = 0; kctl && i < kctl->count; i++) { 193821949f00STakashi Iwai err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); 19395b0cb1d8SJaroslav Kysela if (err < 0) 19405b0cb1d8SJaroslav Kysela return err; 19415b0cb1d8SJaroslav Kysela } 19425b0cb1d8SJaroslav Kysela 19435b0cb1d8SJaroslav Kysela /* other nid->control mapping */ 19445b0cb1d8SJaroslav Kysela for (i = 0; i < spec->num_mixers; i++) { 19455b0cb1d8SJaroslav Kysela for (knew = spec->mixers[i]; knew->name; knew++) { 19465b0cb1d8SJaroslav Kysela if (knew->iface != NID_MAPPING) 19475b0cb1d8SJaroslav Kysela continue; 19485b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, knew->name); 19495b0cb1d8SJaroslav Kysela if (kctl == NULL) 19505b0cb1d8SJaroslav Kysela continue; 19515b0cb1d8SJaroslav Kysela err = snd_hda_add_nid(codec, kctl, 0, 19525b0cb1d8SJaroslav Kysela knew->subdevice); 19535b0cb1d8SJaroslav Kysela } 19545b0cb1d8SJaroslav Kysela } 19555b0cb1d8SJaroslav Kysela 195617314379SLydia Wang /* init power states */ 195717314379SLydia Wang set_jack_power_state(codec); 195817314379SLydia Wang analog_low_current_mode(codec, 1); 195917314379SLydia Wang 1960603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1961c577b8a1SJoseph Chan return 0; 1962c577b8a1SJoseph Chan } 1963c577b8a1SJoseph Chan 1964c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1965c577b8a1SJoseph Chan { 1966c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1967c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1968c577b8a1SJoseph Chan 1969c577b8a1SJoseph Chan codec->num_pcms = 1; 1970c577b8a1SJoseph Chan codec->pcm_info = info; 1971c577b8a1SJoseph Chan 1972c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 1973377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1974377ff31aSLydia Wang *(spec->stream_analog_playback); 1975377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1976377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1977c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); 1978c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 1979c577b8a1SJoseph Chan 1980c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1981c577b8a1SJoseph Chan spec->multiout.max_channels; 1982c577b8a1SJoseph Chan 1983c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1984c577b8a1SJoseph Chan codec->num_pcms++; 1985c577b8a1SJoseph Chan info++; 1986c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 19877ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1988c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1989c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1990c577b8a1SJoseph Chan *(spec->stream_digital_playback); 1991c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1992c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1993c577b8a1SJoseph Chan } 1994c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1995c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 1996c577b8a1SJoseph Chan *(spec->stream_digital_capture); 1997c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1998c577b8a1SJoseph Chan spec->dig_in_nid; 1999c577b8a1SJoseph Chan } 2000c577b8a1SJoseph Chan } 2001c577b8a1SJoseph Chan 2002c577b8a1SJoseph Chan return 0; 2003c577b8a1SJoseph Chan } 2004c577b8a1SJoseph Chan 2005c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 2006c577b8a1SJoseph Chan { 2007c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2008c577b8a1SJoseph Chan 2009c577b8a1SJoseph Chan if (!spec) 2010c577b8a1SJoseph Chan return; 2011c577b8a1SJoseph Chan 2012603c4019STakashi Iwai via_free_kctls(codec); 20131f2e99feSLydia Wang vt1708_stop_hp_work(spec); 2014c577b8a1SJoseph Chan kfree(codec->spec); 2015c577b8a1SJoseph Chan } 2016c577b8a1SJoseph Chan 201769e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 201869e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 201969e52a80SHarald Welte { 2020dcf34c8cSLydia Wang unsigned int present = 0; 202169e52a80SHarald Welte struct via_spec *spec = codec->spec; 202269e52a80SHarald Welte 2023d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 2024dcf34c8cSLydia Wang 2025dcf34c8cSLydia Wang if (!spec->hp_independent_mode) { 2026dcf34c8cSLydia Wang struct snd_ctl_elem_id id; 2027dcf34c8cSLydia Wang /* auto mute */ 2028dcf34c8cSLydia Wang snd_hda_codec_amp_stereo( 2029dcf34c8cSLydia Wang codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0, 2030dcf34c8cSLydia Wang HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); 2031dcf34c8cSLydia Wang /* notify change */ 2032dcf34c8cSLydia Wang memset(&id, 0, sizeof(id)); 2033dcf34c8cSLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 2034dcf34c8cSLydia Wang strcpy(id.name, "Front Playback Switch"); 2035dcf34c8cSLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 2036dcf34c8cSLydia Wang &id); 2037dcf34c8cSLydia Wang } 203869e52a80SHarald Welte } 203969e52a80SHarald Welte 2040f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */ 2041f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec) 2042f3db423dSLydia Wang { 2043f3db423dSLydia Wang unsigned int hp_present, lineout_present; 2044f3db423dSLydia Wang struct via_spec *spec = codec->spec; 2045f3db423dSLydia Wang 2046f3db423dSLydia Wang if (spec->codec_type != VT1716S) 2047f3db423dSLydia Wang return; 2048f3db423dSLydia Wang 2049d56757abSTakashi Iwai lineout_present = snd_hda_jack_detect(codec, 2050d56757abSTakashi Iwai spec->autocfg.line_out_pins[0]); 2051f3db423dSLydia Wang 2052f3db423dSLydia Wang /* Mute Mono Out if Line Out is plugged */ 2053f3db423dSLydia Wang if (lineout_present) { 2054f3db423dSLydia Wang snd_hda_codec_amp_stereo( 2055f3db423dSLydia Wang codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); 2056f3db423dSLydia Wang return; 2057f3db423dSLydia Wang } 2058f3db423dSLydia Wang 2059d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 2060f3db423dSLydia Wang 2061f3db423dSLydia Wang if (!spec->hp_independent_mode) 2062f3db423dSLydia Wang snd_hda_codec_amp_stereo( 2063f3db423dSLydia Wang codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, 2064f3db423dSLydia Wang hp_present ? HDA_AMP_MUTE : 0); 2065f3db423dSLydia Wang } 2066f3db423dSLydia Wang 206769e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 206869e52a80SHarald Welte { 206969e52a80SHarald Welte unsigned int gpio_data; 207069e52a80SHarald Welte unsigned int vol_counter; 207169e52a80SHarald Welte unsigned int vol; 207269e52a80SHarald Welte unsigned int master_vol; 207369e52a80SHarald Welte 207469e52a80SHarald Welte struct via_spec *spec = codec->spec; 207569e52a80SHarald Welte 207669e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 207769e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 207869e52a80SHarald Welte 207969e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 208069e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 208169e52a80SHarald Welte 208269e52a80SHarald Welte vol = vol_counter & 0x1F; 208369e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 208469e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 208569e52a80SHarald Welte AC_AMP_GET_INPUT); 208669e52a80SHarald Welte 208769e52a80SHarald Welte if (gpio_data == 0x02) { 208869e52a80SHarald Welte /* unmute line out */ 208969e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], 209069e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 209169e52a80SHarald Welte 209269e52a80SHarald Welte if (vol_counter & 0x20) { 209369e52a80SHarald Welte /* decrease volume */ 209469e52a80SHarald Welte if (vol > master_vol) 209569e52a80SHarald Welte vol = master_vol; 209669e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 209769e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 209869e52a80SHarald Welte master_vol-vol); 209969e52a80SHarald Welte } else { 210069e52a80SHarald Welte /* increase volume */ 210169e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 210269e52a80SHarald Welte HDA_AMP_VOLMASK, 210369e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 210469e52a80SHarald Welte (master_vol+vol)); 210569e52a80SHarald Welte } 210669e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 210769e52a80SHarald Welte /* mute line out */ 210869e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 210969e52a80SHarald Welte spec->autocfg.line_out_pins[0], 211069e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 211169e52a80SHarald Welte HDA_AMP_MUTE); 211269e52a80SHarald Welte } 211369e52a80SHarald Welte } 211469e52a80SHarald Welte 211525eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */ 211625eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec) 211725eaba2fSLydia Wang { 211825eaba2fSLydia Wang unsigned int hp_present; 211925eaba2fSLydia Wang struct via_spec *spec = codec->spec; 212025eaba2fSLydia Wang 2121ab6734e7SLydia Wang if (spec->codec_type != VT2002P && spec->codec_type != VT1812) 212225eaba2fSLydia Wang return; 212325eaba2fSLydia Wang 2124d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 212525eaba2fSLydia Wang 212625eaba2fSLydia Wang if (!spec->hp_independent_mode) { 212725eaba2fSLydia Wang struct snd_ctl_elem_id id; 212825eaba2fSLydia Wang snd_hda_codec_amp_stereo( 212925eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0, 213025eaba2fSLydia Wang HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); 213125eaba2fSLydia Wang /* notify change */ 213225eaba2fSLydia Wang memset(&id, 0, sizeof(id)); 213325eaba2fSLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 213425eaba2fSLydia Wang strcpy(id.name, "Speaker Playback Switch"); 213525eaba2fSLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 213625eaba2fSLydia Wang &id); 213725eaba2fSLydia Wang } 213825eaba2fSLydia Wang } 213925eaba2fSLydia Wang 214025eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */ 214125eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec) 214225eaba2fSLydia Wang { 214301a1796bSakpm@linux-foundation.org /* use long instead of int below just to avoid an internal compiler 214401a1796bSakpm@linux-foundation.org * error with gcc 4.0.x 214501a1796bSakpm@linux-foundation.org */ 214601a1796bSakpm@linux-foundation.org unsigned long hp_present, present = 0; 214725eaba2fSLydia Wang struct via_spec *spec = codec->spec; 214825eaba2fSLydia Wang int i; 214925eaba2fSLydia Wang 215025eaba2fSLydia Wang if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) 215125eaba2fSLydia Wang return; 215225eaba2fSLydia Wang 2153d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 215425eaba2fSLydia Wang 2155d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]); 215625eaba2fSLydia Wang 215725eaba2fSLydia Wang if (!spec->hp_independent_mode) { 215825eaba2fSLydia Wang /* Mute Line-Outs */ 215925eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 216025eaba2fSLydia Wang snd_hda_codec_amp_stereo( 216125eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 216225eaba2fSLydia Wang HDA_OUTPUT, 0, 216325eaba2fSLydia Wang HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); 216425eaba2fSLydia Wang if (hp_present) 216525eaba2fSLydia Wang present = hp_present; 216625eaba2fSLydia Wang } 216725eaba2fSLydia Wang /* Speakers */ 216825eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 216925eaba2fSLydia Wang snd_hda_codec_amp_stereo( 217025eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0, 217125eaba2fSLydia Wang HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); 217225eaba2fSLydia Wang } 217325eaba2fSLydia Wang 217425eaba2fSLydia Wang 217569e52a80SHarald Welte /* unsolicited event for jack sensing */ 217669e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 217769e52a80SHarald Welte unsigned int res) 217869e52a80SHarald Welte { 217969e52a80SHarald Welte res >>= 26; 2180a34df19aSLydia Wang if (res & VIA_HP_EVENT) 218169e52a80SHarald Welte via_hp_automute(codec); 2182a34df19aSLydia Wang if (res & VIA_GPIO_EVENT) 218369e52a80SHarald Welte via_gpio_control(codec); 2184a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 2185a34df19aSLydia Wang set_jack_power_state(codec); 2186f3db423dSLydia Wang if (res & VIA_MONO_EVENT) 2187f3db423dSLydia Wang via_mono_automute(codec); 218825eaba2fSLydia Wang if (res & VIA_SPEAKER_EVENT) 218925eaba2fSLydia Wang via_speaker_automute(codec); 219025eaba2fSLydia Wang if (res & VIA_BIND_HP_EVENT) 219125eaba2fSLydia Wang via_hp_bind_automute(codec); 219269e52a80SHarald Welte } 219369e52a80SHarald Welte 2194c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec) 2195c577b8a1SJoseph Chan { 2196c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 219769e52a80SHarald Welte int i; 219869e52a80SHarald Welte for (i = 0; i < spec->num_iverbs; i++) 219969e52a80SHarald Welte snd_hda_sequence_write(codec, spec->init_verbs[i]); 220069e52a80SHarald Welte 2201518bf3baSLydia Wang spec->codec_type = get_codec_type(codec); 2202518bf3baSLydia Wang if (spec->codec_type == VT1708BCE) 2203518bf3baSLydia Wang spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost 2204518bf3baSLydia Wang same */ 2205f7278fd0SJosepch Chan /* Lydia Add for EAPD enable */ 2206f7278fd0SJosepch Chan if (!spec->dig_in_nid) { /* No Digital In connection */ 220755d1d6c1STakashi Iwai if (spec->dig_in_pin) { 220855d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 2209f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 221012b74c80STakashi Iwai PIN_OUT); 221155d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 2212f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 2213f7278fd0SJosepch Chan } 221412b74c80STakashi Iwai } else /* enable SPDIF-input pin */ 221512b74c80STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 221612b74c80STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 2217f7278fd0SJosepch Chan 22189da29271STakashi Iwai /* assign slave outs */ 22199da29271STakashi Iwai if (spec->slave_dig_outs[0]) 22209da29271STakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 22215691ec7fSHarald Welte 2222c577b8a1SJoseph Chan return 0; 2223c577b8a1SJoseph Chan } 2224c577b8a1SJoseph Chan 22251f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 22261f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 22271f2e99feSLydia Wang { 22281f2e99feSLydia Wang struct via_spec *spec = codec->spec; 22291f2e99feSLydia Wang vt1708_stop_hp_work(spec); 22301f2e99feSLydia Wang return 0; 22311f2e99feSLydia Wang } 22321f2e99feSLydia Wang #endif 22331f2e99feSLydia Wang 2234cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2235cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 2236cb53c626STakashi Iwai { 2237cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 2238cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 2239cb53c626STakashi Iwai } 2240cb53c626STakashi Iwai #endif 2241cb53c626STakashi Iwai 2242c577b8a1SJoseph Chan /* 2243c577b8a1SJoseph Chan */ 2244c577b8a1SJoseph Chan static struct hda_codec_ops via_patch_ops = { 2245c577b8a1SJoseph Chan .build_controls = via_build_controls, 2246c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 2247c577b8a1SJoseph Chan .init = via_init, 2248c577b8a1SJoseph Chan .free = via_free, 22491f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 22501f2e99feSLydia Wang .suspend = via_suspend, 22511f2e99feSLydia Wang #endif 2252cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2253cb53c626STakashi Iwai .check_power_status = via_check_power_status, 2254cb53c626STakashi Iwai #endif 2255c577b8a1SJoseph Chan }; 2256c577b8a1SJoseph Chan 2257c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */ 2258c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec, 2259c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2260c577b8a1SJoseph Chan { 2261c577b8a1SJoseph Chan int i; 2262c577b8a1SJoseph Chan hda_nid_t nid; 2263c577b8a1SJoseph Chan 2264c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; 2265c577b8a1SJoseph Chan 2266c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 2267c577b8a1SJoseph Chan 2268c577b8a1SJoseph Chan for (i = 0; i < 4; i++) { 2269c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2270c577b8a1SJoseph Chan if (nid) { 2271c577b8a1SJoseph Chan /* config dac list */ 2272c577b8a1SJoseph Chan switch (i) { 2273c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2274c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2275c577b8a1SJoseph Chan break; 2276c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2277c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2278c577b8a1SJoseph Chan break; 2279c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2280fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 2281c577b8a1SJoseph Chan break; 2282c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 2283fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x13; 2284c577b8a1SJoseph Chan break; 2285c577b8a1SJoseph Chan } 2286c577b8a1SJoseph Chan } 2287c577b8a1SJoseph Chan } 2288c577b8a1SJoseph Chan 2289c577b8a1SJoseph Chan return 0; 2290c577b8a1SJoseph Chan } 2291c577b8a1SJoseph Chan 2292c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 2293c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, 2294c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2295c577b8a1SJoseph Chan { 2296c577b8a1SJoseph Chan char name[32]; 2297ea734963STakashi Iwai static const char * const chname[4] = { 2298ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 2299ea734963STakashi Iwai }; 23009645c203SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b}; 2301c577b8a1SJoseph Chan int i, err; 2302c577b8a1SJoseph Chan 2303c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 2304c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2305c577b8a1SJoseph Chan 2306c577b8a1SJoseph Chan if (!nid) 2307c577b8a1SJoseph Chan continue; 2308c577b8a1SJoseph Chan 23099645c203SLydia Wang nid_vol = nid_vols[i]; 2310c577b8a1SJoseph Chan 2311c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 2312c577b8a1SJoseph Chan /* Center/LFE */ 2313c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2314c577b8a1SJoseph Chan "Center Playback Volume", 2315f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2316f7278fd0SJosepch Chan HDA_OUTPUT)); 2317c577b8a1SJoseph Chan if (err < 0) 2318c577b8a1SJoseph Chan return err; 2319c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2320c577b8a1SJoseph Chan "LFE Playback Volume", 2321f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2322f7278fd0SJosepch Chan HDA_OUTPUT)); 2323c577b8a1SJoseph Chan if (err < 0) 2324c577b8a1SJoseph Chan return err; 2325c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2326c577b8a1SJoseph Chan "Center Playback Switch", 2327f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2328f7278fd0SJosepch Chan HDA_OUTPUT)); 2329c577b8a1SJoseph Chan if (err < 0) 2330c577b8a1SJoseph Chan return err; 2331c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2332c577b8a1SJoseph Chan "LFE Playback Switch", 2333f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2334f7278fd0SJosepch Chan HDA_OUTPUT)); 2335c577b8a1SJoseph Chan if (err < 0) 2336c577b8a1SJoseph Chan return err; 2337c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT) { 2338c577b8a1SJoseph Chan /* add control to mixer index 0 */ 2339c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2340c577b8a1SJoseph Chan "Master Front Playback Volume", 23419645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2342f7278fd0SJosepch Chan HDA_INPUT)); 2343c577b8a1SJoseph Chan if (err < 0) 2344c577b8a1SJoseph Chan return err; 2345c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2346c577b8a1SJoseph Chan "Master Front Playback Switch", 23479645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2348f7278fd0SJosepch Chan HDA_INPUT)); 2349c577b8a1SJoseph Chan if (err < 0) 2350c577b8a1SJoseph Chan return err; 2351c577b8a1SJoseph Chan 2352c577b8a1SJoseph Chan /* add control to PW3 */ 2353c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2354c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2355f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2356f7278fd0SJosepch Chan HDA_OUTPUT)); 2357c577b8a1SJoseph Chan if (err < 0) 2358c577b8a1SJoseph Chan return err; 2359c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2360c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2361f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2362f7278fd0SJosepch Chan HDA_OUTPUT)); 2363c577b8a1SJoseph Chan if (err < 0) 2364c577b8a1SJoseph Chan return err; 2365c577b8a1SJoseph Chan } else { 2366c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2367c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2368f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2369f7278fd0SJosepch Chan HDA_OUTPUT)); 2370c577b8a1SJoseph Chan if (err < 0) 2371c577b8a1SJoseph Chan return err; 2372c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2373c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2374f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2375f7278fd0SJosepch Chan HDA_OUTPUT)); 2376c577b8a1SJoseph Chan if (err < 0) 2377c577b8a1SJoseph Chan return err; 2378c577b8a1SJoseph Chan } 2379c577b8a1SJoseph Chan } 2380c577b8a1SJoseph Chan 2381c577b8a1SJoseph Chan return 0; 2382c577b8a1SJoseph Chan } 2383c577b8a1SJoseph Chan 23840aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 23850aa62aefSHarald Welte { 23860aa62aefSHarald Welte int i; 23870aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 2388ea734963STakashi Iwai static const char * const texts[] = { "OFF", "ON", NULL}; 23890aa62aefSHarald Welte 23900aa62aefSHarald Welte /* for hp mode select */ 239110a20af7STakashi Iwai for (i = 0; texts[i]; i++) 239210a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 23930aa62aefSHarald Welte 23940aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 23950aa62aefSHarald Welte } 23960aa62aefSHarald Welte 2397c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 2398c577b8a1SJoseph Chan { 2399c577b8a1SJoseph Chan int err; 2400c577b8a1SJoseph Chan 2401c577b8a1SJoseph Chan if (!pin) 2402c577b8a1SJoseph Chan return 0; 2403c577b8a1SJoseph Chan 2404c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ 2405cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 2406c577b8a1SJoseph Chan 2407c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2408c577b8a1SJoseph Chan "Headphone Playback Volume", 2409c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2410c577b8a1SJoseph Chan if (err < 0) 2411c577b8a1SJoseph Chan return err; 2412c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2413c577b8a1SJoseph Chan "Headphone Playback Switch", 2414c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2415c577b8a1SJoseph Chan if (err < 0) 2416c577b8a1SJoseph Chan return err; 2417c577b8a1SJoseph Chan 24180aa62aefSHarald Welte create_hp_imux(spec); 24190aa62aefSHarald Welte 2420c577b8a1SJoseph Chan return 0; 2421c577b8a1SJoseph Chan } 2422c577b8a1SJoseph Chan 2423c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 242410a20af7STakashi Iwai static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, 2425f3268512STakashi Iwai const struct auto_pin_cfg *cfg, 2426f3268512STakashi Iwai hda_nid_t cap_nid, 2427f3268512STakashi Iwai hda_nid_t pin_idxs[], int num_idxs) 2428c577b8a1SJoseph Chan { 242910a20af7STakashi Iwai struct via_spec *spec = codec->spec; 24300aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 24317b315bb4STakashi Iwai int i, err, idx, type, type_idx = 0; 2432c577b8a1SJoseph Chan 2433c577b8a1SJoseph Chan /* for internal loopback recording select */ 2434f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) { 2435f3268512STakashi Iwai if (pin_idxs[idx] == 0xff) { 243610a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); 2437f3268512STakashi Iwai break; 2438f3268512STakashi Iwai } 2439f3268512STakashi Iwai } 2440c577b8a1SJoseph Chan 24417b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 244210a20af7STakashi Iwai const char *label; 24437b315bb4STakashi Iwai type = cfg->inputs[i].type; 2444f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) 24457b315bb4STakashi Iwai if (pin_idxs[idx] == cfg->inputs[i].pin) 2446c577b8a1SJoseph Chan break; 2447f3268512STakashi Iwai if (idx >= num_idxs) 2448f3268512STakashi Iwai continue; 24497b315bb4STakashi Iwai if (i > 0 && type == cfg->inputs[i - 1].type) 24507b315bb4STakashi Iwai type_idx++; 24517b315bb4STakashi Iwai else 24527b315bb4STakashi Iwai type_idx = 0; 245310a20af7STakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 245410a20af7STakashi Iwai err = via_new_analog_input(spec, label, type_idx, idx, cap_nid); 2455c577b8a1SJoseph Chan if (err < 0) 2456c577b8a1SJoseph Chan return err; 245710a20af7STakashi Iwai snd_hda_add_imux_item(imux, label, idx, NULL); 2458c577b8a1SJoseph Chan } 2459c577b8a1SJoseph Chan return 0; 2460c577b8a1SJoseph Chan } 2461c577b8a1SJoseph Chan 2462f3268512STakashi Iwai /* create playback/capture controls for input pins */ 246310a20af7STakashi Iwai static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec, 2464f3268512STakashi Iwai const struct auto_pin_cfg *cfg) 2465f3268512STakashi Iwai { 2466f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 }; 246710a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs, 2468f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 2469f3268512STakashi Iwai } 2470f3268512STakashi Iwai 2471cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2472cb53c626STakashi Iwai static struct hda_amp_list vt1708_loopbacks[] = { 2473cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 2474cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 2475cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 2476cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 2477cb53c626STakashi Iwai { } /* end */ 2478cb53c626STakashi Iwai }; 2479cb53c626STakashi Iwai #endif 2480cb53c626STakashi Iwai 248176d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 248276d9b0ddSHarald Welte { 248376d9b0ddSHarald Welte unsigned int def_conf; 248476d9b0ddSHarald Welte unsigned char seqassoc; 248576d9b0ddSHarald Welte 24862f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 248776d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 248876d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 248982ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 249082ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 249176d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 24922f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 249376d9b0ddSHarald Welte } 249476d9b0ddSHarald Welte 249576d9b0ddSHarald Welte return; 249676d9b0ddSHarald Welte } 249776d9b0ddSHarald Welte 24981f2e99feSLydia Wang static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, 24991f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 25001f2e99feSLydia Wang { 25011f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 25021f2e99feSLydia Wang struct via_spec *spec = codec->spec; 25031f2e99feSLydia Wang 25041f2e99feSLydia Wang if (spec->codec_type != VT1708) 25051f2e99feSLydia Wang return 0; 25061f2e99feSLydia Wang spec->vt1708_jack_detectect = 25071f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 25081f2e99feSLydia Wang ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect; 25091f2e99feSLydia Wang return 0; 25101f2e99feSLydia Wang } 25111f2e99feSLydia Wang 25121f2e99feSLydia Wang static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, 25131f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 25141f2e99feSLydia Wang { 25151f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 25161f2e99feSLydia Wang struct via_spec *spec = codec->spec; 25171f2e99feSLydia Wang int change; 25181f2e99feSLydia Wang 25191f2e99feSLydia Wang if (spec->codec_type != VT1708) 25201f2e99feSLydia Wang return 0; 25211f2e99feSLydia Wang spec->vt1708_jack_detectect = ucontrol->value.integer.value[0]; 25221f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 25231f2e99feSLydia Wang == !spec->vt1708_jack_detectect; 25241f2e99feSLydia Wang if (spec->vt1708_jack_detectect) { 25251f2e99feSLydia Wang mute_aa_path(codec, 1); 25261f2e99feSLydia Wang notify_aa_path_ctls(codec); 25271f2e99feSLydia Wang } 25281f2e99feSLydia Wang return change; 25291f2e99feSLydia Wang } 25301f2e99feSLydia Wang 25311f2e99feSLydia Wang static struct snd_kcontrol_new vt1708_jack_detectect[] = { 25321f2e99feSLydia Wang { 25331f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 25341f2e99feSLydia Wang .name = "Jack Detect", 25351f2e99feSLydia Wang .count = 1, 25361f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 25371f2e99feSLydia Wang .get = vt1708_jack_detectect_get, 25381f2e99feSLydia Wang .put = vt1708_jack_detectect_put, 25391f2e99feSLydia Wang }, 25401f2e99feSLydia Wang {} /* end */ 25411f2e99feSLydia Wang }; 25421f2e99feSLydia Wang 2543c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec) 2544c577b8a1SJoseph Chan { 2545c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2546c577b8a1SJoseph Chan int err; 2547c577b8a1SJoseph Chan 254876d9b0ddSHarald Welte /* Add HP and CD pin config connect bit re-config action */ 254976d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 255076d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 255176d9b0ddSHarald Welte 2552c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2553c577b8a1SJoseph Chan if (err < 0) 2554c577b8a1SJoseph Chan return err; 2555c577b8a1SJoseph Chan err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg); 2556c577b8a1SJoseph Chan if (err < 0) 2557c577b8a1SJoseph Chan return err; 2558c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2559c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2560c577b8a1SJoseph Chan 2561c577b8a1SJoseph Chan err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg); 2562c577b8a1SJoseph Chan if (err < 0) 2563c577b8a1SJoseph Chan return err; 2564c577b8a1SJoseph Chan err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2565c577b8a1SJoseph Chan if (err < 0) 2566c577b8a1SJoseph Chan return err; 256710a20af7STakashi Iwai err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg); 2568c577b8a1SJoseph Chan if (err < 0) 2569c577b8a1SJoseph Chan return err; 25701f2e99feSLydia Wang /* add jack detect on/off control */ 25711f2e99feSLydia Wang err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect); 25721f2e99feSLydia Wang if (err < 0) 25731f2e99feSLydia Wang return err; 2574c577b8a1SJoseph Chan 2575c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2576c577b8a1SJoseph Chan 25770852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2578c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; 257955d1d6c1STakashi Iwai spec->dig_in_pin = VT1708_DIGIN_PIN; 2580c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2581c577b8a1SJoseph Chan spec->dig_in_nid = VT1708_DIGIN_NID; 2582c577b8a1SJoseph Chan 2583603c4019STakashi Iwai if (spec->kctls.list) 2584603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2585c577b8a1SJoseph Chan 258669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; 2587c577b8a1SJoseph Chan 25880aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 25890aa62aefSHarald Welte 2590f8fdd495SHarald Welte if (spec->hp_mux) 25913d83e577STakashi Iwai via_hp_build(codec); 2592c577b8a1SJoseph Chan 25935b0cb1d8SJaroslav Kysela via_smart51_build(spec); 2594c577b8a1SJoseph Chan return 1; 2595c577b8a1SJoseph Chan } 2596c577b8a1SJoseph Chan 2597c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */ 2598c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec) 2599c577b8a1SJoseph Chan { 260025eaba2fSLydia Wang struct via_spec *spec = codec->spec; 260125eaba2fSLydia Wang 2602c577b8a1SJoseph Chan via_init(codec); 2603c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2604c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 2605c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 2606ab6734e7SLydia Wang if (spec->codec_type == VT2002P || spec->codec_type == VT1812) { 260725eaba2fSLydia Wang via_hp_bind_automute(codec); 260825eaba2fSLydia Wang } else { 260925eaba2fSLydia Wang via_hp_automute(codec); 261025eaba2fSLydia Wang via_speaker_automute(codec); 261125eaba2fSLydia Wang } 261225eaba2fSLydia Wang 2613c577b8a1SJoseph Chan return 0; 2614c577b8a1SJoseph Chan } 2615c577b8a1SJoseph Chan 26161f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 26171f2e99feSLydia Wang { 26181f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 26191f2e99feSLydia Wang vt1708_hp_work.work); 26201f2e99feSLydia Wang if (spec->codec_type != VT1708) 26211f2e99feSLydia Wang return; 26221f2e99feSLydia Wang /* if jack state toggled */ 26231f2e99feSLydia Wang if (spec->vt1708_hp_present 2624d56757abSTakashi Iwai != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { 26251f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 26261f2e99feSLydia Wang via_hp_automute(spec->codec); 26271f2e99feSLydia Wang } 26281f2e99feSLydia Wang vt1708_start_hp_work(spec); 26291f2e99feSLydia Wang } 26301f2e99feSLydia Wang 2631337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2632337b9d02STakashi Iwai { 2633337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2634337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2635337b9d02STakashi Iwai unsigned int type; 2636337b9d02STakashi Iwai int i, n; 2637337b9d02STakashi Iwai 2638337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2639337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2640337b9d02STakashi Iwai while (nid) { 2641a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 26421c55d521STakashi Iwai if (type == AC_WID_PIN) 26431c55d521STakashi Iwai break; 2644337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2645337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2646337b9d02STakashi Iwai if (n <= 0) 2647337b9d02STakashi Iwai break; 2648337b9d02STakashi Iwai if (n > 1) { 2649337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2650337b9d02STakashi Iwai break; 2651337b9d02STakashi Iwai } 2652337b9d02STakashi Iwai nid = conn[0]; 2653337b9d02STakashi Iwai } 2654337b9d02STakashi Iwai } 26551c55d521STakashi Iwai return 0; 2656337b9d02STakashi Iwai } 2657337b9d02STakashi Iwai 2658c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2659c577b8a1SJoseph Chan { 2660c577b8a1SJoseph Chan struct via_spec *spec; 2661c577b8a1SJoseph Chan int err; 2662c577b8a1SJoseph Chan 2663c577b8a1SJoseph Chan /* create a codec specific record */ 26645b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2665c577b8a1SJoseph Chan if (spec == NULL) 2666c577b8a1SJoseph Chan return -ENOMEM; 2667c577b8a1SJoseph Chan 2668c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 2669c577b8a1SJoseph Chan err = vt1708_parse_auto_config(codec); 2670c577b8a1SJoseph Chan if (err < 0) { 2671c577b8a1SJoseph Chan via_free(codec); 2672c577b8a1SJoseph Chan return err; 2673c577b8a1SJoseph Chan } else if (!err) { 2674c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2675c577b8a1SJoseph Chan "from BIOS. Using genenic mode...\n"); 2676c577b8a1SJoseph Chan } 2677c577b8a1SJoseph Chan 2678c577b8a1SJoseph Chan 2679c577b8a1SJoseph Chan spec->stream_name_analog = "VT1708 Analog"; 2680c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1708_pcm_analog_playback; 2681bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2682bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2683bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2684c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1708_pcm_analog_capture; 2685c577b8a1SJoseph Chan 2686c577b8a1SJoseph Chan spec->stream_name_digital = "VT1708 Digital"; 2687c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1708_pcm_digital_playback; 2688c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1708_pcm_digital_capture; 2689c577b8a1SJoseph Chan 2690c577b8a1SJoseph Chan 2691c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2692c577b8a1SJoseph Chan spec->adc_nids = vt1708_adc_nids; 2693c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); 26940f67a611STakashi Iwai get_mux_nids(codec); 2695c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1708_capture_mixer; 2696c577b8a1SJoseph Chan spec->num_mixers++; 2697c577b8a1SJoseph Chan } 2698c577b8a1SJoseph Chan 2699c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2700c577b8a1SJoseph Chan 2701c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 2702cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2703cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 2704cb53c626STakashi Iwai #endif 27051f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2706c577b8a1SJoseph Chan return 0; 2707c577b8a1SJoseph Chan } 2708c577b8a1SJoseph Chan 2709c577b8a1SJoseph Chan /* capture mixer elements */ 2710c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1709_capture_mixer[] = { 2711c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), 2712c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), 2713c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), 2714c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), 2715c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), 2716c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), 2717c577b8a1SJoseph Chan { 2718c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2719c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 2720c577b8a1SJoseph Chan * So call somewhat different.. 2721c577b8a1SJoseph Chan */ 2722c577b8a1SJoseph Chan /* .name = "Capture Source", */ 2723c577b8a1SJoseph Chan .name = "Input Source", 2724c577b8a1SJoseph Chan .count = 1, 2725c577b8a1SJoseph Chan .info = via_mux_enum_info, 2726c577b8a1SJoseph Chan .get = via_mux_enum_get, 2727c577b8a1SJoseph Chan .put = via_mux_enum_put, 2728c577b8a1SJoseph Chan }, 2729c577b8a1SJoseph Chan { } /* end */ 2730c577b8a1SJoseph Chan }; 2731c577b8a1SJoseph Chan 273269e52a80SHarald Welte static struct hda_verb vt1709_uniwill_init_verbs[] = { 2733a34df19aSLydia Wang {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 2734a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 273569e52a80SHarald Welte { } 273669e52a80SHarald Welte }; 273769e52a80SHarald Welte 2738c577b8a1SJoseph Chan /* 2739c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2740c577b8a1SJoseph Chan */ 2741c577b8a1SJoseph Chan static struct hda_verb vt1709_10ch_volume_init_verbs[] = { 2742c577b8a1SJoseph Chan /* 2743c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2744c577b8a1SJoseph Chan */ 2745c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2746c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2747c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2748c577b8a1SJoseph Chan 2749c577b8a1SJoseph Chan 2750f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2751c577b8a1SJoseph Chan * mixer widget 2752c577b8a1SJoseph Chan */ 2753c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2754f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2755f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2756f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2757f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2758f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2759c577b8a1SJoseph Chan 2760c577b8a1SJoseph Chan /* 2761c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2762c577b8a1SJoseph Chan */ 2763c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2764c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2765c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2766c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2767c577b8a1SJoseph Chan 2768c577b8a1SJoseph Chan /* 2769c577b8a1SJoseph Chan * Unmute PW3 and PW4 2770c577b8a1SJoseph Chan */ 2771c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2772c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2773c577b8a1SJoseph Chan 2774bfdc675aSLydia Wang /* Set input of PW4 as MW0 */ 2775bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2776c577b8a1SJoseph Chan /* PW9 Output enable */ 2777c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2778c577b8a1SJoseph Chan { } 2779c577b8a1SJoseph Chan }; 2780c577b8a1SJoseph Chan 2781c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { 2782c577b8a1SJoseph Chan .substreams = 1, 2783c577b8a1SJoseph Chan .channels_min = 2, 2784c577b8a1SJoseph Chan .channels_max = 10, 2785c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2786c577b8a1SJoseph Chan .ops = { 2787c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2788c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2789c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2790c577b8a1SJoseph Chan }, 2791c577b8a1SJoseph Chan }; 2792c577b8a1SJoseph Chan 2793c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { 2794c577b8a1SJoseph Chan .substreams = 1, 2795c577b8a1SJoseph Chan .channels_min = 2, 2796c577b8a1SJoseph Chan .channels_max = 6, 2797c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2798c577b8a1SJoseph Chan .ops = { 2799c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2800c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2801c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2802c577b8a1SJoseph Chan }, 2803c577b8a1SJoseph Chan }; 2804c577b8a1SJoseph Chan 2805c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_analog_capture = { 2806c577b8a1SJoseph Chan .substreams = 2, 2807c577b8a1SJoseph Chan .channels_min = 2, 2808c577b8a1SJoseph Chan .channels_max = 2, 2809c577b8a1SJoseph Chan .nid = 0x14, /* NID to query formats and rates */ 2810c577b8a1SJoseph Chan .ops = { 2811c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 2812c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 2813c577b8a1SJoseph Chan }, 2814c577b8a1SJoseph Chan }; 2815c577b8a1SJoseph Chan 2816c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_playback = { 2817c577b8a1SJoseph Chan .substreams = 1, 2818c577b8a1SJoseph Chan .channels_min = 2, 2819c577b8a1SJoseph Chan .channels_max = 2, 2820c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 2821c577b8a1SJoseph Chan .ops = { 2822c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 2823c577b8a1SJoseph Chan .close = via_dig_playback_pcm_close 2824c577b8a1SJoseph Chan }, 2825c577b8a1SJoseph Chan }; 2826c577b8a1SJoseph Chan 2827c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_capture = { 2828c577b8a1SJoseph Chan .substreams = 1, 2829c577b8a1SJoseph Chan .channels_min = 2, 2830c577b8a1SJoseph Chan .channels_max = 2, 2831c577b8a1SJoseph Chan }; 2832c577b8a1SJoseph Chan 2833c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec, 2834c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2835c577b8a1SJoseph Chan { 2836c577b8a1SJoseph Chan int i; 2837c577b8a1SJoseph Chan hda_nid_t nid; 2838c577b8a1SJoseph Chan 2839c577b8a1SJoseph Chan if (cfg->line_outs == 4) /* 10 channels */ 2840c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */ 2841c577b8a1SJoseph Chan else if (cfg->line_outs == 3) /* 6 channels */ 2842c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */ 2843c577b8a1SJoseph Chan 2844c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 2845c577b8a1SJoseph Chan 2846c577b8a1SJoseph Chan if (cfg->line_outs == 4) { /* 10 channels */ 2847c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 2848c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2849c577b8a1SJoseph Chan if (nid) { 2850c577b8a1SJoseph Chan /* config dac list */ 2851c577b8a1SJoseph Chan switch (i) { 2852c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2853c577b8a1SJoseph Chan /* AOW0 */ 2854c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2855c577b8a1SJoseph Chan break; 2856c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2857c577b8a1SJoseph Chan /* AOW2 */ 2858c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2859c577b8a1SJoseph Chan break; 2860c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2861c577b8a1SJoseph Chan /* AOW3 */ 2862fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 2863c577b8a1SJoseph Chan break; 2864c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 2865c577b8a1SJoseph Chan /* AOW1 */ 2866fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x27; 2867c577b8a1SJoseph Chan break; 2868c577b8a1SJoseph Chan default: 2869c577b8a1SJoseph Chan break; 2870c577b8a1SJoseph Chan } 2871c577b8a1SJoseph Chan } 2872c577b8a1SJoseph Chan } 2873c577b8a1SJoseph Chan spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ 2874c577b8a1SJoseph Chan 2875c577b8a1SJoseph Chan } else if (cfg->line_outs == 3) { /* 6 channels */ 2876c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 2877c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2878c577b8a1SJoseph Chan if (nid) { 2879c577b8a1SJoseph Chan /* config dac list */ 2880c577b8a1SJoseph Chan switch (i) { 2881c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2882c577b8a1SJoseph Chan /* AOW0 */ 2883c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2884c577b8a1SJoseph Chan break; 2885c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2886c577b8a1SJoseph Chan /* AOW2 */ 2887c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2888c577b8a1SJoseph Chan break; 2889c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2890c577b8a1SJoseph Chan /* AOW1 */ 2891c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x11; 2892c577b8a1SJoseph Chan break; 2893c577b8a1SJoseph Chan default: 2894c577b8a1SJoseph Chan break; 2895c577b8a1SJoseph Chan } 2896c577b8a1SJoseph Chan } 2897c577b8a1SJoseph Chan } 2898c577b8a1SJoseph Chan } 2899c577b8a1SJoseph Chan 2900c577b8a1SJoseph Chan return 0; 2901c577b8a1SJoseph Chan } 2902c577b8a1SJoseph Chan 2903c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 2904c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, 2905c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2906c577b8a1SJoseph Chan { 2907c577b8a1SJoseph Chan char name[32]; 2908ea734963STakashi Iwai static const char * const chname[4] = { 2909ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 2910ea734963STakashi Iwai }; 29114483a2f5SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29}; 2912c577b8a1SJoseph Chan int i, err; 2913c577b8a1SJoseph Chan 2914c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 2915c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2916c577b8a1SJoseph Chan 2917c577b8a1SJoseph Chan if (!nid) 2918c577b8a1SJoseph Chan continue; 2919c577b8a1SJoseph Chan 29204483a2f5SLydia Wang nid_vol = nid_vols[i]; 29214483a2f5SLydia Wang 2922c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 2923c577b8a1SJoseph Chan /* Center/LFE */ 2924c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2925c577b8a1SJoseph Chan "Center Playback Volume", 29264483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2927f7278fd0SJosepch Chan HDA_OUTPUT)); 2928c577b8a1SJoseph Chan if (err < 0) 2929c577b8a1SJoseph Chan return err; 2930c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2931c577b8a1SJoseph Chan "LFE Playback Volume", 29324483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2933f7278fd0SJosepch Chan HDA_OUTPUT)); 2934c577b8a1SJoseph Chan if (err < 0) 2935c577b8a1SJoseph Chan return err; 2936c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2937c577b8a1SJoseph Chan "Center Playback Switch", 29384483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2939f7278fd0SJosepch Chan HDA_OUTPUT)); 2940c577b8a1SJoseph Chan if (err < 0) 2941c577b8a1SJoseph Chan return err; 2942c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2943c577b8a1SJoseph Chan "LFE Playback Switch", 29444483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2945f7278fd0SJosepch Chan HDA_OUTPUT)); 2946c577b8a1SJoseph Chan if (err < 0) 2947c577b8a1SJoseph Chan return err; 2948c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT) { 29494483a2f5SLydia Wang /* ADD control to mixer index 0 */ 2950c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2951c577b8a1SJoseph Chan "Master Front Playback Volume", 29524483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2953f7278fd0SJosepch Chan HDA_INPUT)); 2954c577b8a1SJoseph Chan if (err < 0) 2955c577b8a1SJoseph Chan return err; 2956c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2957c577b8a1SJoseph Chan "Master Front Playback Switch", 29584483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2959f7278fd0SJosepch Chan HDA_INPUT)); 2960c577b8a1SJoseph Chan if (err < 0) 2961c577b8a1SJoseph Chan return err; 2962c577b8a1SJoseph Chan 2963c577b8a1SJoseph Chan /* add control to PW3 */ 2964c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2965c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2966f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2967f7278fd0SJosepch Chan HDA_OUTPUT)); 2968c577b8a1SJoseph Chan if (err < 0) 2969c577b8a1SJoseph Chan return err; 2970c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2971c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2972f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2973f7278fd0SJosepch Chan HDA_OUTPUT)); 2974c577b8a1SJoseph Chan if (err < 0) 2975c577b8a1SJoseph Chan return err; 2976c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SURROUND) { 2977c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2978c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 29794483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2980f7278fd0SJosepch Chan HDA_OUTPUT)); 2981c577b8a1SJoseph Chan if (err < 0) 2982c577b8a1SJoseph Chan return err; 2983c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2984c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 29854483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2986f7278fd0SJosepch Chan HDA_OUTPUT)); 2987c577b8a1SJoseph Chan if (err < 0) 2988c577b8a1SJoseph Chan return err; 2989c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SIDE) { 2990c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2991c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 29924483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2993f7278fd0SJosepch Chan HDA_OUTPUT)); 2994c577b8a1SJoseph Chan if (err < 0) 2995c577b8a1SJoseph Chan return err; 2996c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2997c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 29984483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2999f7278fd0SJosepch Chan HDA_OUTPUT)); 3000c577b8a1SJoseph Chan if (err < 0) 3001c577b8a1SJoseph Chan return err; 3002c577b8a1SJoseph Chan } 3003c577b8a1SJoseph Chan } 3004c577b8a1SJoseph Chan 3005c577b8a1SJoseph Chan return 0; 3006c577b8a1SJoseph Chan } 3007c577b8a1SJoseph Chan 3008c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3009c577b8a1SJoseph Chan { 3010c577b8a1SJoseph Chan int err; 3011c577b8a1SJoseph Chan 3012c577b8a1SJoseph Chan if (!pin) 3013c577b8a1SJoseph Chan return 0; 3014c577b8a1SJoseph Chan 3015c577b8a1SJoseph Chan if (spec->multiout.num_dacs == 5) /* 10 channels */ 3016c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1709_HP_DAC_NID; 3017c577b8a1SJoseph Chan else if (spec->multiout.num_dacs == 3) /* 6 channels */ 3018c577b8a1SJoseph Chan spec->multiout.hp_nid = 0; 3019cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3020c577b8a1SJoseph Chan 3021c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3022c577b8a1SJoseph Chan "Headphone Playback Volume", 3023c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3024c577b8a1SJoseph Chan if (err < 0) 3025c577b8a1SJoseph Chan return err; 3026c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3027c577b8a1SJoseph Chan "Headphone Playback Switch", 3028c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3029c577b8a1SJoseph Chan if (err < 0) 3030c577b8a1SJoseph Chan return err; 3031c577b8a1SJoseph Chan 3032c577b8a1SJoseph Chan return 0; 3033c577b8a1SJoseph Chan } 3034c577b8a1SJoseph Chan 3035c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 303610a20af7STakashi Iwai static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec, 3037c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 3038c577b8a1SJoseph Chan { 3039f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 }; 304010a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs, 3041f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 3042c577b8a1SJoseph Chan } 3043c577b8a1SJoseph Chan 3044c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec) 3045c577b8a1SJoseph Chan { 3046c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 3047c577b8a1SJoseph Chan int err; 3048c577b8a1SJoseph Chan 3049c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3050c577b8a1SJoseph Chan if (err < 0) 3051c577b8a1SJoseph Chan return err; 3052c577b8a1SJoseph Chan err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg); 3053c577b8a1SJoseph Chan if (err < 0) 3054c577b8a1SJoseph Chan return err; 3055c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3056c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 3057c577b8a1SJoseph Chan 3058c577b8a1SJoseph Chan err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg); 3059c577b8a1SJoseph Chan if (err < 0) 3060c577b8a1SJoseph Chan return err; 3061c577b8a1SJoseph Chan err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3062c577b8a1SJoseph Chan if (err < 0) 3063c577b8a1SJoseph Chan return err; 306410a20af7STakashi Iwai err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg); 3065c577b8a1SJoseph Chan if (err < 0) 3066c577b8a1SJoseph Chan return err; 3067c577b8a1SJoseph Chan 3068c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3069c577b8a1SJoseph Chan 30700852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 3071c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; 307255d1d6c1STakashi Iwai spec->dig_in_pin = VT1709_DIGIN_PIN; 3073c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 3074c577b8a1SJoseph Chan spec->dig_in_nid = VT1709_DIGIN_NID; 3075c577b8a1SJoseph Chan 3076603c4019STakashi Iwai if (spec->kctls.list) 3077603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3078c577b8a1SJoseph Chan 30790aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 3080c577b8a1SJoseph Chan 3081f8fdd495SHarald Welte if (spec->hp_mux) 30823d83e577STakashi Iwai via_hp_build(codec); 3083f8fdd495SHarald Welte 30845b0cb1d8SJaroslav Kysela via_smart51_build(spec); 3085c577b8a1SJoseph Chan return 1; 3086c577b8a1SJoseph Chan } 3087c577b8a1SJoseph Chan 3088cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 3089cb53c626STakashi Iwai static struct hda_amp_list vt1709_loopbacks[] = { 3090cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 3091cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 3092cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 3093cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 3094cb53c626STakashi Iwai { } /* end */ 3095cb53c626STakashi Iwai }; 3096cb53c626STakashi Iwai #endif 3097cb53c626STakashi Iwai 3098c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 3099c577b8a1SJoseph Chan { 3100c577b8a1SJoseph Chan struct via_spec *spec; 3101c577b8a1SJoseph Chan int err; 3102c577b8a1SJoseph Chan 3103c577b8a1SJoseph Chan /* create a codec specific record */ 31045b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3105c577b8a1SJoseph Chan if (spec == NULL) 3106c577b8a1SJoseph Chan return -ENOMEM; 3107c577b8a1SJoseph Chan 3108c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 3109c577b8a1SJoseph Chan if (err < 0) { 3110c577b8a1SJoseph Chan via_free(codec); 3111c577b8a1SJoseph Chan return err; 3112c577b8a1SJoseph Chan } else if (!err) { 3113c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 3114c577b8a1SJoseph Chan "Using genenic mode...\n"); 3115c577b8a1SJoseph Chan } 3116c577b8a1SJoseph Chan 311769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; 311869e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 3119c577b8a1SJoseph Chan 3120c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 3121c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; 3122c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 3123c577b8a1SJoseph Chan 3124c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 3125c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 3126c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 3127c577b8a1SJoseph Chan 3128c577b8a1SJoseph Chan 3129c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 3130c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 3131c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 3132337b9d02STakashi Iwai get_mux_nids(codec); 3133c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 3134c577b8a1SJoseph Chan spec->num_mixers++; 3135c577b8a1SJoseph Chan } 3136c577b8a1SJoseph Chan 3137c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 3138c577b8a1SJoseph Chan 3139c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 314069e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3141cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 3142cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 3143cb53c626STakashi Iwai #endif 3144c577b8a1SJoseph Chan 3145c577b8a1SJoseph Chan return 0; 3146c577b8a1SJoseph Chan } 3147c577b8a1SJoseph Chan /* 3148c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 3149c577b8a1SJoseph Chan */ 3150c577b8a1SJoseph Chan static struct hda_verb vt1709_6ch_volume_init_verbs[] = { 3151c577b8a1SJoseph Chan /* 3152c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 3153c577b8a1SJoseph Chan */ 3154c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3155c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3156c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3157c577b8a1SJoseph Chan 3158c577b8a1SJoseph Chan 3159c577b8a1SJoseph Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3160c577b8a1SJoseph Chan * mixer widget 3161c577b8a1SJoseph Chan */ 3162c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3163c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3164c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3165c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3166c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3167c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3168c577b8a1SJoseph Chan 3169c577b8a1SJoseph Chan /* 3170c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 3171c577b8a1SJoseph Chan */ 3172c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 3173c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3174c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3175c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3176c577b8a1SJoseph Chan 3177c577b8a1SJoseph Chan /* 3178c577b8a1SJoseph Chan * Unmute PW3 and PW4 3179c577b8a1SJoseph Chan */ 3180c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3181c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3182c577b8a1SJoseph Chan 3183c577b8a1SJoseph Chan /* Set input of PW4 as MW0 */ 3184c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 3185c577b8a1SJoseph Chan /* PW9 Output enable */ 3186c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3187c577b8a1SJoseph Chan { } 3188c577b8a1SJoseph Chan }; 3189c577b8a1SJoseph Chan 3190c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 3191c577b8a1SJoseph Chan { 3192c577b8a1SJoseph Chan struct via_spec *spec; 3193c577b8a1SJoseph Chan int err; 3194c577b8a1SJoseph Chan 3195c577b8a1SJoseph Chan /* create a codec specific record */ 31965b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3197c577b8a1SJoseph Chan if (spec == NULL) 3198c577b8a1SJoseph Chan return -ENOMEM; 3199c577b8a1SJoseph Chan 3200c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 3201c577b8a1SJoseph Chan if (err < 0) { 3202c577b8a1SJoseph Chan via_free(codec); 3203c577b8a1SJoseph Chan return err; 3204c577b8a1SJoseph Chan } else if (!err) { 3205c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 3206c577b8a1SJoseph Chan "Using genenic mode...\n"); 3207c577b8a1SJoseph Chan } 3208c577b8a1SJoseph Chan 320969e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; 321069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 3211c577b8a1SJoseph Chan 3212c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 3213c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; 3214c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 3215c577b8a1SJoseph Chan 3216c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 3217c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 3218c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 3219c577b8a1SJoseph Chan 3220c577b8a1SJoseph Chan 3221c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 3222c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 3223c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 3224337b9d02STakashi Iwai get_mux_nids(codec); 3225c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 3226c577b8a1SJoseph Chan spec->num_mixers++; 3227c577b8a1SJoseph Chan } 3228c577b8a1SJoseph Chan 3229c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 3230c577b8a1SJoseph Chan 3231c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 323269e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3233cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 3234cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 3235cb53c626STakashi Iwai #endif 3236f7278fd0SJosepch Chan return 0; 3237f7278fd0SJosepch Chan } 3238f7278fd0SJosepch Chan 3239f7278fd0SJosepch Chan /* capture mixer elements */ 3240f7278fd0SJosepch Chan static struct snd_kcontrol_new vt1708B_capture_mixer[] = { 3241f7278fd0SJosepch Chan HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3242f7278fd0SJosepch Chan HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3243f7278fd0SJosepch Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3244f7278fd0SJosepch Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 3245f7278fd0SJosepch Chan { 3246f7278fd0SJosepch Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3247f7278fd0SJosepch Chan /* The multiple "Capture Source" controls confuse alsamixer 3248f7278fd0SJosepch Chan * So call somewhat different.. 3249f7278fd0SJosepch Chan */ 3250f7278fd0SJosepch Chan /* .name = "Capture Source", */ 3251f7278fd0SJosepch Chan .name = "Input Source", 3252f7278fd0SJosepch Chan .count = 1, 3253f7278fd0SJosepch Chan .info = via_mux_enum_info, 3254f7278fd0SJosepch Chan .get = via_mux_enum_get, 3255f7278fd0SJosepch Chan .put = via_mux_enum_put, 3256f7278fd0SJosepch Chan }, 3257f7278fd0SJosepch Chan { } /* end */ 3258f7278fd0SJosepch Chan }; 3259f7278fd0SJosepch Chan /* 3260f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 3261f7278fd0SJosepch Chan */ 3262f7278fd0SJosepch Chan static struct hda_verb vt1708B_8ch_volume_init_verbs[] = { 3263f7278fd0SJosepch Chan /* 3264f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 3265f7278fd0SJosepch Chan */ 3266f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3267f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3268f7278fd0SJosepch Chan 3269f7278fd0SJosepch Chan 3270f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3271f7278fd0SJosepch Chan * mixer widget 3272f7278fd0SJosepch Chan */ 3273f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3274f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3275f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3276f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3277f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3278f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3279f7278fd0SJosepch Chan 3280f7278fd0SJosepch Chan /* 3281f7278fd0SJosepch Chan * Set up output mixers 3282f7278fd0SJosepch Chan */ 3283f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 3284f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3285f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3286f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3287f7278fd0SJosepch Chan 3288f7278fd0SJosepch Chan /* Setup default input to PW4 */ 3289bfdc675aSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, 3290f7278fd0SJosepch Chan /* PW9 Output enable */ 3291f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3292f7278fd0SJosepch Chan /* PW10 Input enable */ 3293f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 3294f7278fd0SJosepch Chan { } 3295f7278fd0SJosepch Chan }; 3296f7278fd0SJosepch Chan 3297f7278fd0SJosepch Chan static struct hda_verb vt1708B_4ch_volume_init_verbs[] = { 3298f7278fd0SJosepch Chan /* 3299f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 3300f7278fd0SJosepch Chan */ 3301f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3302f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3303f7278fd0SJosepch Chan 3304f7278fd0SJosepch Chan 3305f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3306f7278fd0SJosepch Chan * mixer widget 3307f7278fd0SJosepch Chan */ 3308f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3309f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3310f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3311f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3312f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3313f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3314f7278fd0SJosepch Chan 3315f7278fd0SJosepch Chan /* 3316f7278fd0SJosepch Chan * Set up output mixers 3317f7278fd0SJosepch Chan */ 3318f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 3319f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3320f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3321f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3322f7278fd0SJosepch Chan 3323f7278fd0SJosepch Chan /* Setup default input of PW4 to MW0 */ 3324f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 3325f7278fd0SJosepch Chan /* PW9 Output enable */ 3326f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3327f7278fd0SJosepch Chan /* PW10 Input enable */ 3328f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 3329f7278fd0SJosepch Chan { } 3330f7278fd0SJosepch Chan }; 3331f7278fd0SJosepch Chan 333269e52a80SHarald Welte static struct hda_verb vt1708B_uniwill_init_verbs[] = { 3333a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3334a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3335a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3336a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3337a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3338a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3339a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3340a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3341a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 334269e52a80SHarald Welte { } 334369e52a80SHarald Welte }; 334469e52a80SHarald Welte 334517314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo, 334617314379SLydia Wang struct hda_codec *codec, 334717314379SLydia Wang struct snd_pcm_substream *substream) 334817314379SLydia Wang { 334917314379SLydia Wang int idle = substream->pstr->substream_opened == 1 335017314379SLydia Wang && substream->ref_count == 0; 335117314379SLydia Wang 335217314379SLydia Wang analog_low_current_mode(codec, idle); 335317314379SLydia Wang return 0; 335417314379SLydia Wang } 335517314379SLydia Wang 3356f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { 33570aa62aefSHarald Welte .substreams = 2, 3358f7278fd0SJosepch Chan .channels_min = 2, 3359f7278fd0SJosepch Chan .channels_max = 8, 3360f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 3361f7278fd0SJosepch Chan .ops = { 3362f7278fd0SJosepch Chan .open = via_playback_pcm_open, 33630aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 336417314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 336517314379SLydia Wang .close = via_pcm_open_close 3366f7278fd0SJosepch Chan }, 3367f7278fd0SJosepch Chan }; 3368f7278fd0SJosepch Chan 3369f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { 33700aa62aefSHarald Welte .substreams = 2, 3371f7278fd0SJosepch Chan .channels_min = 2, 3372f7278fd0SJosepch Chan .channels_max = 4, 3373f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 3374f7278fd0SJosepch Chan .ops = { 3375f7278fd0SJosepch Chan .open = via_playback_pcm_open, 33760aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 33770aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 3378f7278fd0SJosepch Chan }, 3379f7278fd0SJosepch Chan }; 3380f7278fd0SJosepch Chan 3381f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_analog_capture = { 3382f7278fd0SJosepch Chan .substreams = 2, 3383f7278fd0SJosepch Chan .channels_min = 2, 3384f7278fd0SJosepch Chan .channels_max = 2, 3385f7278fd0SJosepch Chan .nid = 0x13, /* NID to query formats and rates */ 3386f7278fd0SJosepch Chan .ops = { 338717314379SLydia Wang .open = via_pcm_open_close, 3388f7278fd0SJosepch Chan .prepare = via_capture_pcm_prepare, 338917314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 339017314379SLydia Wang .close = via_pcm_open_close 3391f7278fd0SJosepch Chan }, 3392f7278fd0SJosepch Chan }; 3393f7278fd0SJosepch Chan 3394f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_playback = { 3395f7278fd0SJosepch Chan .substreams = 1, 3396f7278fd0SJosepch Chan .channels_min = 2, 3397f7278fd0SJosepch Chan .channels_max = 2, 3398f7278fd0SJosepch Chan /* NID is set in via_build_pcms */ 3399f7278fd0SJosepch Chan .ops = { 3400f7278fd0SJosepch Chan .open = via_dig_playback_pcm_open, 3401f7278fd0SJosepch Chan .close = via_dig_playback_pcm_close, 34029da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 34039da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3404f7278fd0SJosepch Chan }, 3405f7278fd0SJosepch Chan }; 3406f7278fd0SJosepch Chan 3407f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_capture = { 3408f7278fd0SJosepch Chan .substreams = 1, 3409f7278fd0SJosepch Chan .channels_min = 2, 3410f7278fd0SJosepch Chan .channels_max = 2, 3411f7278fd0SJosepch Chan }; 3412f7278fd0SJosepch Chan 3413f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */ 3414f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, 3415f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3416f7278fd0SJosepch Chan { 3417f7278fd0SJosepch Chan int i; 3418f7278fd0SJosepch Chan hda_nid_t nid; 3419f7278fd0SJosepch Chan 3420f7278fd0SJosepch Chan spec->multiout.num_dacs = cfg->line_outs; 3421f7278fd0SJosepch Chan 3422f7278fd0SJosepch Chan spec->multiout.dac_nids = spec->private_dac_nids; 3423f7278fd0SJosepch Chan 3424f7278fd0SJosepch Chan for (i = 0; i < 4; i++) { 3425f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 3426f7278fd0SJosepch Chan if (nid) { 3427f7278fd0SJosepch Chan /* config dac list */ 3428f7278fd0SJosepch Chan switch (i) { 3429f7278fd0SJosepch Chan case AUTO_SEQ_FRONT: 3430f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x10; 3431f7278fd0SJosepch Chan break; 3432f7278fd0SJosepch Chan case AUTO_SEQ_CENLFE: 3433f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x24; 3434f7278fd0SJosepch Chan break; 3435f7278fd0SJosepch Chan case AUTO_SEQ_SURROUND: 3436fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 3437f7278fd0SJosepch Chan break; 3438f7278fd0SJosepch Chan case AUTO_SEQ_SIDE: 3439fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x25; 3440f7278fd0SJosepch Chan break; 3441f7278fd0SJosepch Chan } 3442f7278fd0SJosepch Chan } 3443f7278fd0SJosepch Chan } 3444f7278fd0SJosepch Chan 3445f7278fd0SJosepch Chan return 0; 3446f7278fd0SJosepch Chan } 3447f7278fd0SJosepch Chan 3448f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */ 3449f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, 3450f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3451f7278fd0SJosepch Chan { 3452f7278fd0SJosepch Chan char name[32]; 3453ea734963STakashi Iwai static const char * const chname[4] = { 3454ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 3455ea734963STakashi Iwai }; 3456fb4cb772SHarald Welte hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27}; 3457f7278fd0SJosepch Chan hda_nid_t nid, nid_vol = 0; 3458f7278fd0SJosepch Chan int i, err; 3459f7278fd0SJosepch Chan 3460f7278fd0SJosepch Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3461f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 3462f7278fd0SJosepch Chan 3463f7278fd0SJosepch Chan if (!nid) 3464f7278fd0SJosepch Chan continue; 3465f7278fd0SJosepch Chan 3466f7278fd0SJosepch Chan nid_vol = nid_vols[i]; 3467f7278fd0SJosepch Chan 3468f7278fd0SJosepch Chan if (i == AUTO_SEQ_CENLFE) { 3469f7278fd0SJosepch Chan /* Center/LFE */ 3470f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3471f7278fd0SJosepch Chan "Center Playback Volume", 3472f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3473f7278fd0SJosepch Chan HDA_OUTPUT)); 3474f7278fd0SJosepch Chan if (err < 0) 3475f7278fd0SJosepch Chan return err; 3476f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3477f7278fd0SJosepch Chan "LFE Playback Volume", 3478f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3479f7278fd0SJosepch Chan HDA_OUTPUT)); 3480f7278fd0SJosepch Chan if (err < 0) 3481f7278fd0SJosepch Chan return err; 3482f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3483f7278fd0SJosepch Chan "Center Playback Switch", 3484f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3485f7278fd0SJosepch Chan HDA_OUTPUT)); 3486f7278fd0SJosepch Chan if (err < 0) 3487f7278fd0SJosepch Chan return err; 3488f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3489f7278fd0SJosepch Chan "LFE Playback Switch", 3490f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3491f7278fd0SJosepch Chan HDA_OUTPUT)); 3492f7278fd0SJosepch Chan if (err < 0) 3493f7278fd0SJosepch Chan return err; 3494f7278fd0SJosepch Chan } else if (i == AUTO_SEQ_FRONT) { 3495f7278fd0SJosepch Chan /* add control to mixer index 0 */ 3496f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3497f7278fd0SJosepch Chan "Master Front Playback Volume", 3498f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3499f7278fd0SJosepch Chan HDA_INPUT)); 3500f7278fd0SJosepch Chan if (err < 0) 3501f7278fd0SJosepch Chan return err; 3502f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3503f7278fd0SJosepch Chan "Master Front Playback Switch", 3504f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3505f7278fd0SJosepch Chan HDA_INPUT)); 3506f7278fd0SJosepch Chan if (err < 0) 3507f7278fd0SJosepch Chan return err; 3508f7278fd0SJosepch Chan 3509f7278fd0SJosepch Chan /* add control to PW3 */ 3510f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3511f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3512f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3513f7278fd0SJosepch Chan HDA_OUTPUT)); 3514f7278fd0SJosepch Chan if (err < 0) 3515f7278fd0SJosepch Chan return err; 3516f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3517f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3518f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3519f7278fd0SJosepch Chan HDA_OUTPUT)); 3520f7278fd0SJosepch Chan if (err < 0) 3521f7278fd0SJosepch Chan return err; 3522f7278fd0SJosepch Chan } else { 3523f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3524f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3525f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3526f7278fd0SJosepch Chan HDA_OUTPUT)); 3527f7278fd0SJosepch Chan if (err < 0) 3528f7278fd0SJosepch Chan return err; 3529f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3530f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3531f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3532f7278fd0SJosepch Chan HDA_OUTPUT)); 3533f7278fd0SJosepch Chan if (err < 0) 3534f7278fd0SJosepch Chan return err; 3535f7278fd0SJosepch Chan } 3536f7278fd0SJosepch Chan } 3537f7278fd0SJosepch Chan 3538f7278fd0SJosepch Chan return 0; 3539f7278fd0SJosepch Chan } 3540f7278fd0SJosepch Chan 3541f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3542f7278fd0SJosepch Chan { 3543f7278fd0SJosepch Chan int err; 3544f7278fd0SJosepch Chan 3545f7278fd0SJosepch Chan if (!pin) 3546f7278fd0SJosepch Chan return 0; 3547f7278fd0SJosepch Chan 3548f7278fd0SJosepch Chan spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ 3549cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3550f7278fd0SJosepch Chan 3551f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3552f7278fd0SJosepch Chan "Headphone Playback Volume", 3553f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3554f7278fd0SJosepch Chan if (err < 0) 3555f7278fd0SJosepch Chan return err; 3556f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3557f7278fd0SJosepch Chan "Headphone Playback Switch", 3558f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3559f7278fd0SJosepch Chan if (err < 0) 3560f7278fd0SJosepch Chan return err; 3561f7278fd0SJosepch Chan 35620aa62aefSHarald Welte create_hp_imux(spec); 35630aa62aefSHarald Welte 3564f7278fd0SJosepch Chan return 0; 3565f7278fd0SJosepch Chan } 3566f7278fd0SJosepch Chan 3567f7278fd0SJosepch Chan /* create playback/capture controls for input pins */ 356810a20af7STakashi Iwai static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec, 3569f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3570f7278fd0SJosepch Chan { 3571f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e }; 357210a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, 3573f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 3574f7278fd0SJosepch Chan } 3575f7278fd0SJosepch Chan 3576f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec) 3577f7278fd0SJosepch Chan { 3578f7278fd0SJosepch Chan struct via_spec *spec = codec->spec; 3579f7278fd0SJosepch Chan int err; 3580f7278fd0SJosepch Chan 3581f7278fd0SJosepch Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3582f7278fd0SJosepch Chan if (err < 0) 3583f7278fd0SJosepch Chan return err; 3584f7278fd0SJosepch Chan err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); 3585f7278fd0SJosepch Chan if (err < 0) 3586f7278fd0SJosepch Chan return err; 3587f7278fd0SJosepch Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3588f7278fd0SJosepch Chan return 0; /* can't find valid BIOS pin config */ 3589f7278fd0SJosepch Chan 3590f7278fd0SJosepch Chan err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); 3591f7278fd0SJosepch Chan if (err < 0) 3592f7278fd0SJosepch Chan return err; 3593f7278fd0SJosepch Chan err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3594f7278fd0SJosepch Chan if (err < 0) 3595f7278fd0SJosepch Chan return err; 359610a20af7STakashi Iwai err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg); 3597f7278fd0SJosepch Chan if (err < 0) 3598f7278fd0SJosepch Chan return err; 3599f7278fd0SJosepch Chan 3600f7278fd0SJosepch Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3601f7278fd0SJosepch Chan 36020852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 3603f7278fd0SJosepch Chan spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; 360455d1d6c1STakashi Iwai spec->dig_in_pin = VT1708B_DIGIN_PIN; 3605f7278fd0SJosepch Chan if (spec->autocfg.dig_in_pin) 3606f7278fd0SJosepch Chan spec->dig_in_nid = VT1708B_DIGIN_NID; 3607f7278fd0SJosepch Chan 3608603c4019STakashi Iwai if (spec->kctls.list) 3609603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3610f7278fd0SJosepch Chan 36110aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 36120aa62aefSHarald Welte 3613f8fdd495SHarald Welte if (spec->hp_mux) 36143d83e577STakashi Iwai via_hp_build(codec); 3615f7278fd0SJosepch Chan 36165b0cb1d8SJaroslav Kysela via_smart51_build(spec); 3617f7278fd0SJosepch Chan return 1; 3618f7278fd0SJosepch Chan } 3619f7278fd0SJosepch Chan 3620f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3621f7278fd0SJosepch Chan static struct hda_amp_list vt1708B_loopbacks[] = { 3622f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 3623f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 3624f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 3625f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 3626f7278fd0SJosepch Chan { } /* end */ 3627f7278fd0SJosepch Chan }; 3628f7278fd0SJosepch Chan #endif 3629518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 3630f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 3631f7278fd0SJosepch Chan { 3632f7278fd0SJosepch Chan struct via_spec *spec; 3633f7278fd0SJosepch Chan int err; 3634f7278fd0SJosepch Chan 3635518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 3636518bf3baSLydia Wang return patch_vt1708S(codec); 3637f7278fd0SJosepch Chan /* create a codec specific record */ 36385b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3639f7278fd0SJosepch Chan if (spec == NULL) 3640f7278fd0SJosepch Chan return -ENOMEM; 3641f7278fd0SJosepch Chan 3642f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3643f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3644f7278fd0SJosepch Chan if (err < 0) { 3645f7278fd0SJosepch Chan via_free(codec); 3646f7278fd0SJosepch Chan return err; 3647f7278fd0SJosepch Chan } else if (!err) { 3648f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3649f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3650f7278fd0SJosepch Chan } 3651f7278fd0SJosepch Chan 365269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; 365369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3654f7278fd0SJosepch Chan 3655f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 3656f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; 3657f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3658f7278fd0SJosepch Chan 3659f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 3660f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3661f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3662f7278fd0SJosepch Chan 3663f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 3664f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 3665f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 3666337b9d02STakashi Iwai get_mux_nids(codec); 3667f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3668f7278fd0SJosepch Chan spec->num_mixers++; 3669f7278fd0SJosepch Chan } 3670f7278fd0SJosepch Chan 3671f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3672f7278fd0SJosepch Chan 3673f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 367469e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3675f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3676f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3677f7278fd0SJosepch Chan #endif 3678f7278fd0SJosepch Chan 3679f7278fd0SJosepch Chan return 0; 3680f7278fd0SJosepch Chan } 3681f7278fd0SJosepch Chan 3682f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 3683f7278fd0SJosepch Chan { 3684f7278fd0SJosepch Chan struct via_spec *spec; 3685f7278fd0SJosepch Chan int err; 3686f7278fd0SJosepch Chan 3687f7278fd0SJosepch Chan /* create a codec specific record */ 36885b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3689f7278fd0SJosepch Chan if (spec == NULL) 3690f7278fd0SJosepch Chan return -ENOMEM; 3691f7278fd0SJosepch Chan 3692f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3693f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3694f7278fd0SJosepch Chan if (err < 0) { 3695f7278fd0SJosepch Chan via_free(codec); 3696f7278fd0SJosepch Chan return err; 3697f7278fd0SJosepch Chan } else if (!err) { 3698f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3699f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3700f7278fd0SJosepch Chan } 3701f7278fd0SJosepch Chan 370269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; 370369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3704f7278fd0SJosepch Chan 3705f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 3706f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; 3707f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3708f7278fd0SJosepch Chan 3709f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 3710f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3711f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3712f7278fd0SJosepch Chan 3713f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 3714f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 3715f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 3716337b9d02STakashi Iwai get_mux_nids(codec); 3717f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3718f7278fd0SJosepch Chan spec->num_mixers++; 3719f7278fd0SJosepch Chan } 3720f7278fd0SJosepch Chan 3721f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3722f7278fd0SJosepch Chan 3723f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 372469e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3725f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3726f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3727f7278fd0SJosepch Chan #endif 3728c577b8a1SJoseph Chan 3729c577b8a1SJoseph Chan return 0; 3730c577b8a1SJoseph Chan } 3731c577b8a1SJoseph Chan 3732d949cac1SHarald Welte /* Patch for VT1708S */ 3733d949cac1SHarald Welte 3734d949cac1SHarald Welte /* capture mixer elements */ 3735d949cac1SHarald Welte static struct snd_kcontrol_new vt1708S_capture_mixer[] = { 3736d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3737d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3738d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3739d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 37406369bcfcSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 37416369bcfcSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 37426369bcfcSLydia Wang HDA_INPUT), 3743d949cac1SHarald Welte { 3744d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3745d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3746d949cac1SHarald Welte * So call somewhat different.. 3747d949cac1SHarald Welte */ 3748d949cac1SHarald Welte /* .name = "Capture Source", */ 3749d949cac1SHarald Welte .name = "Input Source", 3750d949cac1SHarald Welte .count = 1, 3751d949cac1SHarald Welte .info = via_mux_enum_info, 3752d949cac1SHarald Welte .get = via_mux_enum_get, 3753d949cac1SHarald Welte .put = via_mux_enum_put, 3754d949cac1SHarald Welte }, 3755d949cac1SHarald Welte { } /* end */ 3756d949cac1SHarald Welte }; 3757d949cac1SHarald Welte 3758d949cac1SHarald Welte static struct hda_verb vt1708S_volume_init_verbs[] = { 3759d949cac1SHarald Welte /* Unmute ADC0-1 and set the default input to mic-in */ 3760d949cac1SHarald Welte {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3761d949cac1SHarald Welte {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3762d949cac1SHarald Welte 3763d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the 3764d949cac1SHarald Welte * analog-loopback mixer widget */ 3765d949cac1SHarald Welte /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3766d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3767d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3768d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3769d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3770d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3771d949cac1SHarald Welte 3772d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3773d949cac1SHarald Welte {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 37745691ec7fSHarald Welte /* PW9, PW10 Output enable */ 3775d949cac1SHarald Welte {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 37765691ec7fSHarald Welte {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3777d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 3778d7426329SHarald Welte {0x1, 0xf98, 0x1}, 3779bc7e7e5cSLydia Wang /* don't bybass mixer */ 3780bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 3781d949cac1SHarald Welte { } 3782d949cac1SHarald Welte }; 3783d949cac1SHarald Welte 378469e52a80SHarald Welte static struct hda_verb vt1708S_uniwill_init_verbs[] = { 3785a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3786a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3787a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3788a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3789a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3790a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3791a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3792a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3793a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 379469e52a80SHarald Welte { } 379569e52a80SHarald Welte }; 379669e52a80SHarald Welte 3797d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_playback = { 3798d949cac1SHarald Welte .substreams = 2, 3799d949cac1SHarald Welte .channels_min = 2, 3800d949cac1SHarald Welte .channels_max = 8, 3801d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 3802d949cac1SHarald Welte .ops = { 3803d949cac1SHarald Welte .open = via_playback_pcm_open, 3804c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 3805c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 380617314379SLydia Wang .close = via_pcm_open_close 3807d949cac1SHarald Welte }, 3808d949cac1SHarald Welte }; 3809d949cac1SHarald Welte 3810d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_capture = { 3811d949cac1SHarald Welte .substreams = 2, 3812d949cac1SHarald Welte .channels_min = 2, 3813d949cac1SHarald Welte .channels_max = 2, 3814d949cac1SHarald Welte .nid = 0x13, /* NID to query formats and rates */ 3815d949cac1SHarald Welte .ops = { 381617314379SLydia Wang .open = via_pcm_open_close, 3817d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 381817314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 381917314379SLydia Wang .close = via_pcm_open_close 3820d949cac1SHarald Welte }, 3821d949cac1SHarald Welte }; 3822d949cac1SHarald Welte 3823d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_digital_playback = { 38249da29271STakashi Iwai .substreams = 1, 3825d949cac1SHarald Welte .channels_min = 2, 3826d949cac1SHarald Welte .channels_max = 2, 3827d949cac1SHarald Welte /* NID is set in via_build_pcms */ 3828d949cac1SHarald Welte .ops = { 3829d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 3830d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 38319da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 38329da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3833d949cac1SHarald Welte }, 3834d949cac1SHarald Welte }; 3835d949cac1SHarald Welte 3836d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 3837d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec, 3838d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3839d949cac1SHarald Welte { 3840d949cac1SHarald Welte int i; 3841d949cac1SHarald Welte hda_nid_t nid; 3842d949cac1SHarald Welte 3843d949cac1SHarald Welte spec->multiout.num_dacs = cfg->line_outs; 3844d949cac1SHarald Welte 3845d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 3846d949cac1SHarald Welte 3847d949cac1SHarald Welte for (i = 0; i < 4; i++) { 3848d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3849d949cac1SHarald Welte if (nid) { 3850d949cac1SHarald Welte /* config dac list */ 3851d949cac1SHarald Welte switch (i) { 3852d949cac1SHarald Welte case AUTO_SEQ_FRONT: 3853d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x10; 3854d949cac1SHarald Welte break; 3855d949cac1SHarald Welte case AUTO_SEQ_CENLFE: 3856d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x24; 3857d949cac1SHarald Welte break; 3858d949cac1SHarald Welte case AUTO_SEQ_SURROUND: 3859d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x11; 3860d949cac1SHarald Welte break; 3861d949cac1SHarald Welte case AUTO_SEQ_SIDE: 3862d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x25; 3863d949cac1SHarald Welte break; 3864d949cac1SHarald Welte } 3865d949cac1SHarald Welte } 3866d949cac1SHarald Welte } 3867d949cac1SHarald Welte 386832e0191dSClemens Ladisch /* for Smart 5.1, line/mic inputs double as output pins */ 386932e0191dSClemens Ladisch if (cfg->line_outs == 1) { 387032e0191dSClemens Ladisch spec->multiout.num_dacs = 3; 387132e0191dSClemens Ladisch spec->multiout.dac_nids[AUTO_SEQ_SURROUND] = 0x11; 387232e0191dSClemens Ladisch spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x24; 387332e0191dSClemens Ladisch } 387432e0191dSClemens Ladisch 3875d949cac1SHarald Welte return 0; 3876d949cac1SHarald Welte } 3877d949cac1SHarald Welte 3878d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 3879d949cac1SHarald Welte static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec, 3880d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3881d949cac1SHarald Welte { 3882d949cac1SHarald Welte char name[32]; 3883ea734963STakashi Iwai static const char * const chname[4] = { 3884ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 3885ea734963STakashi Iwai }; 3886d949cac1SHarald Welte hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25}; 3887d949cac1SHarald Welte hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27}; 3888d949cac1SHarald Welte hda_nid_t nid, nid_vol, nid_mute; 3889d949cac1SHarald Welte int i, err; 3890d949cac1SHarald Welte 3891d949cac1SHarald Welte for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3892d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3893d949cac1SHarald Welte 389432e0191dSClemens Ladisch /* for Smart 5.1, there are always at least six channels */ 389532e0191dSClemens Ladisch if (!nid && i > AUTO_SEQ_CENLFE) 3896d949cac1SHarald Welte continue; 3897d949cac1SHarald Welte 3898d949cac1SHarald Welte nid_vol = nid_vols[i]; 3899d949cac1SHarald Welte nid_mute = nid_mutes[i]; 3900d949cac1SHarald Welte 3901d949cac1SHarald Welte if (i == AUTO_SEQ_CENLFE) { 3902d949cac1SHarald Welte /* Center/LFE */ 3903d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3904d949cac1SHarald Welte "Center Playback Volume", 3905d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3906d949cac1SHarald Welte HDA_OUTPUT)); 3907d949cac1SHarald Welte if (err < 0) 3908d949cac1SHarald Welte return err; 3909d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3910d949cac1SHarald Welte "LFE Playback Volume", 3911d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3912d949cac1SHarald Welte HDA_OUTPUT)); 3913d949cac1SHarald Welte if (err < 0) 3914d949cac1SHarald Welte return err; 3915d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3916d949cac1SHarald Welte "Center Playback Switch", 3917d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3918d949cac1SHarald Welte 1, 0, 3919d949cac1SHarald Welte HDA_OUTPUT)); 3920d949cac1SHarald Welte if (err < 0) 3921d949cac1SHarald Welte return err; 3922d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3923d949cac1SHarald Welte "LFE Playback Switch", 3924d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3925d949cac1SHarald Welte 2, 0, 3926d949cac1SHarald Welte HDA_OUTPUT)); 3927d949cac1SHarald Welte if (err < 0) 3928d949cac1SHarald Welte return err; 3929d949cac1SHarald Welte } else if (i == AUTO_SEQ_FRONT) { 3930d949cac1SHarald Welte /* add control to mixer index 0 */ 3931d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3932d949cac1SHarald Welte "Master Front Playback Volume", 3933d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3934d949cac1SHarald Welte HDA_INPUT)); 3935d949cac1SHarald Welte if (err < 0) 3936d949cac1SHarald Welte return err; 3937d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3938d949cac1SHarald Welte "Master Front Playback Switch", 3939d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3940d949cac1SHarald Welte HDA_INPUT)); 3941d949cac1SHarald Welte if (err < 0) 3942d949cac1SHarald Welte return err; 3943d949cac1SHarald Welte 3944d949cac1SHarald Welte /* Front */ 3945d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3946d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3947d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3948d949cac1SHarald Welte HDA_OUTPUT)); 3949d949cac1SHarald Welte if (err < 0) 3950d949cac1SHarald Welte return err; 3951d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 3952d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3953d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3954d949cac1SHarald Welte 3, 0, 3955d949cac1SHarald Welte HDA_OUTPUT)); 3956d949cac1SHarald Welte if (err < 0) 3957d949cac1SHarald Welte return err; 3958d949cac1SHarald Welte } else { 3959d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3960d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3961d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3962d949cac1SHarald Welte HDA_OUTPUT)); 3963d949cac1SHarald Welte if (err < 0) 3964d949cac1SHarald Welte return err; 3965d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 3966d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3967d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3968d949cac1SHarald Welte 3, 0, 3969d949cac1SHarald Welte HDA_OUTPUT)); 3970d949cac1SHarald Welte if (err < 0) 3971d949cac1SHarald Welte return err; 3972d949cac1SHarald Welte } 3973d949cac1SHarald Welte } 3974d949cac1SHarald Welte 3975d949cac1SHarald Welte return 0; 3976d949cac1SHarald Welte } 3977d949cac1SHarald Welte 3978d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3979d949cac1SHarald Welte { 3980d949cac1SHarald Welte int err; 3981d949cac1SHarald Welte 3982d949cac1SHarald Welte if (!pin) 3983d949cac1SHarald Welte return 0; 3984d949cac1SHarald Welte 3985d949cac1SHarald Welte spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ 3986cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3987d949cac1SHarald Welte 3988d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3989d949cac1SHarald Welte "Headphone Playback Volume", 3990d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 3991d949cac1SHarald Welte if (err < 0) 3992d949cac1SHarald Welte return err; 3993d949cac1SHarald Welte 3994d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3995d949cac1SHarald Welte "Headphone Playback Switch", 3996d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3997d949cac1SHarald Welte if (err < 0) 3998d949cac1SHarald Welte return err; 3999d949cac1SHarald Welte 40000aa62aefSHarald Welte create_hp_imux(spec); 40010aa62aefSHarald Welte 4002d949cac1SHarald Welte return 0; 4003d949cac1SHarald Welte } 4004d949cac1SHarald Welte 4005d949cac1SHarald Welte /* create playback/capture controls for input pins */ 400610a20af7STakashi Iwai static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec, 4007d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4008d949cac1SHarald Welte { 4009f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; 401010a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, 4011f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 4012d949cac1SHarald Welte } 4013d949cac1SHarald Welte 40149da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 40159da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 40169da29271STakashi Iwai { 40179da29271STakashi Iwai struct via_spec *spec = codec->spec; 40189da29271STakashi Iwai int i; 40199da29271STakashi Iwai 40209da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 40219da29271STakashi Iwai hda_nid_t nid; 40229da29271STakashi Iwai int conn; 40239da29271STakashi Iwai 40249da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 40259da29271STakashi Iwai if (!nid) 40269da29271STakashi Iwai continue; 40279da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 40289da29271STakashi Iwai if (conn < 1) 40299da29271STakashi Iwai continue; 40309da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 40319da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 40329da29271STakashi Iwai else { 40339da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 40349da29271STakashi Iwai break; /* at most two dig outs */ 40359da29271STakashi Iwai } 40369da29271STakashi Iwai } 40379da29271STakashi Iwai } 40389da29271STakashi Iwai 4039d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec) 4040d949cac1SHarald Welte { 4041d949cac1SHarald Welte struct via_spec *spec = codec->spec; 4042d949cac1SHarald Welte int err; 4043d949cac1SHarald Welte 40449da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4045d949cac1SHarald Welte if (err < 0) 4046d949cac1SHarald Welte return err; 4047d949cac1SHarald Welte err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); 4048d949cac1SHarald Welte if (err < 0) 4049d949cac1SHarald Welte return err; 4050d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4051d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 4052d949cac1SHarald Welte 4053d949cac1SHarald Welte err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg); 4054d949cac1SHarald Welte if (err < 0) 4055d949cac1SHarald Welte return err; 4056d949cac1SHarald Welte err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4057d949cac1SHarald Welte if (err < 0) 4058d949cac1SHarald Welte return err; 405910a20af7STakashi Iwai err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg); 4060d949cac1SHarald Welte if (err < 0) 4061d949cac1SHarald Welte return err; 4062d949cac1SHarald Welte 4063d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4064d949cac1SHarald Welte 40659da29271STakashi Iwai fill_dig_outs(codec); 406698aa34c0SHarald Welte 4067603c4019STakashi Iwai if (spec->kctls.list) 4068603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 4069d949cac1SHarald Welte 40700aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 40710aa62aefSHarald Welte 4072f8fdd495SHarald Welte if (spec->hp_mux) 40733d83e577STakashi Iwai via_hp_build(codec); 4074d949cac1SHarald Welte 40755b0cb1d8SJaroslav Kysela via_smart51_build(spec); 4076d949cac1SHarald Welte return 1; 4077d949cac1SHarald Welte } 4078d949cac1SHarald Welte 4079d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4080d949cac1SHarald Welte static struct hda_amp_list vt1708S_loopbacks[] = { 4081d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 4082d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 4083d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 4084d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 4085d949cac1SHarald Welte { } /* end */ 4086d949cac1SHarald Welte }; 4087d949cac1SHarald Welte #endif 4088d949cac1SHarald Welte 40896369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 40906369bcfcSLydia Wang int offset, int num_steps, int step_size) 40916369bcfcSLydia Wang { 40926369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 40936369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 40946369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 40956369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 40966369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 40976369bcfcSLydia Wang } 40986369bcfcSLydia Wang 4099d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 4100d949cac1SHarald Welte { 4101d949cac1SHarald Welte struct via_spec *spec; 4102d949cac1SHarald Welte int err; 4103d949cac1SHarald Welte 4104d949cac1SHarald Welte /* create a codec specific record */ 41055b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4106d949cac1SHarald Welte if (spec == NULL) 4107d949cac1SHarald Welte return -ENOMEM; 4108d949cac1SHarald Welte 4109d949cac1SHarald Welte /* automatic parse from the BIOS config */ 4110d949cac1SHarald Welte err = vt1708S_parse_auto_config(codec); 4111d949cac1SHarald Welte if (err < 0) { 4112d949cac1SHarald Welte via_free(codec); 4113d949cac1SHarald Welte return err; 4114d949cac1SHarald Welte } else if (!err) { 4115d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 4116d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 4117d949cac1SHarald Welte } 4118d949cac1SHarald Welte 411969e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; 412069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs; 4121d949cac1SHarald Welte 412236dd5c4aSLydia Wang if (codec->vendor_id == 0x11060440) 412336dd5c4aSLydia Wang spec->stream_name_analog = "VT1818S Analog"; 412436dd5c4aSLydia Wang else 4125d949cac1SHarald Welte spec->stream_name_analog = "VT1708S Analog"; 4126d949cac1SHarald Welte spec->stream_analog_playback = &vt1708S_pcm_analog_playback; 4127d949cac1SHarald Welte spec->stream_analog_capture = &vt1708S_pcm_analog_capture; 4128d949cac1SHarald Welte 412936dd5c4aSLydia Wang if (codec->vendor_id == 0x11060440) 413036dd5c4aSLydia Wang spec->stream_name_digital = "VT1818S Digital"; 413136dd5c4aSLydia Wang else 4132d949cac1SHarald Welte spec->stream_name_digital = "VT1708S Digital"; 4133d949cac1SHarald Welte spec->stream_digital_playback = &vt1708S_pcm_digital_playback; 4134d949cac1SHarald Welte 4135d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 4136d949cac1SHarald Welte spec->adc_nids = vt1708S_adc_nids; 4137d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); 4138337b9d02STakashi Iwai get_mux_nids(codec); 41396369bcfcSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 41406369bcfcSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 4141d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; 4142d949cac1SHarald Welte spec->num_mixers++; 4143d949cac1SHarald Welte } 4144d949cac1SHarald Welte 4145d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 4146d949cac1SHarald Welte 4147d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 414869e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 4149d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4150d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 4151d949cac1SHarald Welte #endif 4152d949cac1SHarald Welte 4153518bf3baSLydia Wang /* correct names for VT1708BCE */ 4154518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 4155518bf3baSLydia Wang kfree(codec->chip_name); 4156518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 4157518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 4158518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 4159518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 4160518bf3baSLydia Wang spec->stream_name_analog = "VT1708BCE Analog"; 4161518bf3baSLydia Wang spec->stream_name_digital = "VT1708BCE Digital"; 4162518bf3baSLydia Wang } 4163d949cac1SHarald Welte return 0; 4164d949cac1SHarald Welte } 4165d949cac1SHarald Welte 4166d949cac1SHarald Welte /* Patch for VT1702 */ 4167d949cac1SHarald Welte 4168d949cac1SHarald Welte /* capture mixer elements */ 4169d949cac1SHarald Welte static struct snd_kcontrol_new vt1702_capture_mixer[] = { 4170d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), 4171d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), 4172d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), 4173d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), 4174d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), 4175d949cac1SHarald Welte HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), 4176d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, 4177d949cac1SHarald Welte HDA_INPUT), 4178d949cac1SHarald Welte { 4179d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4180d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 4181d949cac1SHarald Welte * So call somewhat different.. 4182d949cac1SHarald Welte */ 4183d949cac1SHarald Welte /* .name = "Capture Source", */ 4184d949cac1SHarald Welte .name = "Input Source", 4185d949cac1SHarald Welte .count = 1, 4186d949cac1SHarald Welte .info = via_mux_enum_info, 4187d949cac1SHarald Welte .get = via_mux_enum_get, 4188d949cac1SHarald Welte .put = via_mux_enum_put, 4189d949cac1SHarald Welte }, 4190d949cac1SHarald Welte { } /* end */ 4191d949cac1SHarald Welte }; 4192d949cac1SHarald Welte 4193d949cac1SHarald Welte static struct hda_verb vt1702_volume_init_verbs[] = { 4194d949cac1SHarald Welte /* 4195d949cac1SHarald Welte * Unmute ADC0-1 and set the default input to mic-in 4196d949cac1SHarald Welte */ 4197d949cac1SHarald Welte {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4198d949cac1SHarald Welte {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4199d949cac1SHarald Welte {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4200d949cac1SHarald Welte 4201d949cac1SHarald Welte 4202d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4203d949cac1SHarald Welte * mixer widget 4204d949cac1SHarald Welte */ 4205d949cac1SHarald Welte /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ 4206d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4207d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4208d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 4209d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 4210d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4211d949cac1SHarald Welte 4212d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 4213d949cac1SHarald Welte {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, 4214d949cac1SHarald Welte /* PW6 PW7 Output enable */ 4215d949cac1SHarald Welte {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4216d949cac1SHarald Welte {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4217bc7e7e5cSLydia Wang /* mixer enable */ 4218bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 4219bc7e7e5cSLydia Wang /* GPIO 0~2 */ 4220bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 4221d949cac1SHarald Welte { } 4222d949cac1SHarald Welte }; 4223d949cac1SHarald Welte 422469e52a80SHarald Welte static struct hda_verb vt1702_uniwill_init_verbs[] = { 4225a34df19aSLydia Wang {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, 4226a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4227a34df19aSLydia Wang {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4228a34df19aSLydia Wang {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4229a34df19aSLydia Wang {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4230a34df19aSLydia Wang {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 423169e52a80SHarald Welte { } 423269e52a80SHarald Welte }; 423369e52a80SHarald Welte 4234d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_playback = { 42350aa62aefSHarald Welte .substreams = 2, 4236d949cac1SHarald Welte .channels_min = 2, 4237d949cac1SHarald Welte .channels_max = 2, 4238d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 4239d949cac1SHarald Welte .ops = { 4240d949cac1SHarald Welte .open = via_playback_pcm_open, 42410aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 424217314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 424317314379SLydia Wang .close = via_pcm_open_close 4244d949cac1SHarald Welte }, 4245d949cac1SHarald Welte }; 4246d949cac1SHarald Welte 4247d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_capture = { 4248d949cac1SHarald Welte .substreams = 3, 4249d949cac1SHarald Welte .channels_min = 2, 4250d949cac1SHarald Welte .channels_max = 2, 4251d949cac1SHarald Welte .nid = 0x12, /* NID to query formats and rates */ 4252d949cac1SHarald Welte .ops = { 425317314379SLydia Wang .open = via_pcm_open_close, 4254d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 425517314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 425617314379SLydia Wang .close = via_pcm_open_close 4257d949cac1SHarald Welte }, 4258d949cac1SHarald Welte }; 4259d949cac1SHarald Welte 4260d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_digital_playback = { 42615691ec7fSHarald Welte .substreams = 2, 4262d949cac1SHarald Welte .channels_min = 2, 4263d949cac1SHarald Welte .channels_max = 2, 4264d949cac1SHarald Welte /* NID is set in via_build_pcms */ 4265d949cac1SHarald Welte .ops = { 4266d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 4267d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 42689da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 42699da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 4270d949cac1SHarald Welte }, 4271d949cac1SHarald Welte }; 4272d949cac1SHarald Welte 4273d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 4274d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec, 4275d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4276d949cac1SHarald Welte { 4277d949cac1SHarald Welte spec->multiout.num_dacs = 1; 4278d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 4279d949cac1SHarald Welte 4280d949cac1SHarald Welte if (cfg->line_out_pins[0]) { 4281d949cac1SHarald Welte /* config dac list */ 4282d949cac1SHarald Welte spec->multiout.dac_nids[0] = 0x10; 4283d949cac1SHarald Welte } 4284d949cac1SHarald Welte 4285d949cac1SHarald Welte return 0; 4286d949cac1SHarald Welte } 4287d949cac1SHarald Welte 4288d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 4289d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, 4290d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4291d949cac1SHarald Welte { 4292d949cac1SHarald Welte int err; 4293d949cac1SHarald Welte 4294d949cac1SHarald Welte if (!cfg->line_out_pins[0]) 4295d949cac1SHarald Welte return -1; 4296d949cac1SHarald Welte 4297d949cac1SHarald Welte /* add control to mixer index 0 */ 4298d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4299d949cac1SHarald Welte "Master Front Playback Volume", 4300d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4301d949cac1SHarald Welte if (err < 0) 4302d949cac1SHarald Welte return err; 4303d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4304d949cac1SHarald Welte "Master Front Playback Switch", 4305d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4306d949cac1SHarald Welte if (err < 0) 4307d949cac1SHarald Welte return err; 4308d949cac1SHarald Welte 4309d949cac1SHarald Welte /* Front */ 4310d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4311d949cac1SHarald Welte "Front Playback Volume", 4312d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT)); 4313d949cac1SHarald Welte if (err < 0) 4314d949cac1SHarald Welte return err; 4315d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4316d949cac1SHarald Welte "Front Playback Switch", 4317d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT)); 4318d949cac1SHarald Welte if (err < 0) 4319d949cac1SHarald Welte return err; 4320d949cac1SHarald Welte 4321d949cac1SHarald Welte return 0; 4322d949cac1SHarald Welte } 4323d949cac1SHarald Welte 4324d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4325d949cac1SHarald Welte { 43260713efebSLydia Wang int err, i; 43270713efebSLydia Wang struct hda_input_mux *imux; 4328ea734963STakashi Iwai static const char * const texts[] = { "ON", "OFF", NULL}; 4329d949cac1SHarald Welte if (!pin) 4330d949cac1SHarald Welte return 0; 4331d949cac1SHarald Welte spec->multiout.hp_nid = 0x1D; 4332cdc1784dSLydia Wang spec->hp_independent_mode_index = 0; 4333d949cac1SHarald Welte 4334d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4335d949cac1SHarald Welte "Headphone Playback Volume", 4336d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT)); 4337d949cac1SHarald Welte if (err < 0) 4338d949cac1SHarald Welte return err; 4339d949cac1SHarald Welte 4340d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4341d949cac1SHarald Welte "Headphone Playback Switch", 4342d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4343d949cac1SHarald Welte if (err < 0) 4344d949cac1SHarald Welte return err; 4345d949cac1SHarald Welte 43460713efebSLydia Wang imux = &spec->private_imux[1]; 43470aa62aefSHarald Welte 43480713efebSLydia Wang /* for hp mode select */ 434910a20af7STakashi Iwai for (i = 0; texts[i]; i++) 435010a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 43510713efebSLydia Wang 43520713efebSLydia Wang spec->hp_mux = &spec->private_imux[1]; 4353d949cac1SHarald Welte return 0; 4354d949cac1SHarald Welte } 4355d949cac1SHarald Welte 4356d949cac1SHarald Welte /* create playback/capture controls for input pins */ 435710a20af7STakashi Iwai static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec, 4358d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4359d949cac1SHarald Welte { 4360f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff }; 436110a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs, 4362f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 4363d949cac1SHarald Welte } 4364d949cac1SHarald Welte 4365d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec) 4366d949cac1SHarald Welte { 4367d949cac1SHarald Welte struct via_spec *spec = codec->spec; 4368d949cac1SHarald Welte int err; 4369d949cac1SHarald Welte 43709da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4371d949cac1SHarald Welte if (err < 0) 4372d949cac1SHarald Welte return err; 4373d949cac1SHarald Welte err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); 4374d949cac1SHarald Welte if (err < 0) 4375d949cac1SHarald Welte return err; 4376d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4377d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 4378d949cac1SHarald Welte 4379d949cac1SHarald Welte err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg); 4380d949cac1SHarald Welte if (err < 0) 4381d949cac1SHarald Welte return err; 4382d949cac1SHarald Welte err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4383d949cac1SHarald Welte if (err < 0) 4384d949cac1SHarald Welte return err; 4385c2c02ea3SLydia Wang /* limit AA path volume to 0 dB */ 4386c2c02ea3SLydia Wang snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 4387c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 4388c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 4389c2c02ea3SLydia Wang (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 4390c2c02ea3SLydia Wang (1 << AC_AMPCAP_MUTE_SHIFT)); 439110a20af7STakashi Iwai err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg); 4392d949cac1SHarald Welte if (err < 0) 4393d949cac1SHarald Welte return err; 4394d949cac1SHarald Welte 4395d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4396d949cac1SHarald Welte 43979da29271STakashi Iwai fill_dig_outs(codec); 439898aa34c0SHarald Welte 4399603c4019STakashi Iwai if (spec->kctls.list) 4400603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 4401d949cac1SHarald Welte 44020aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 44030aa62aefSHarald Welte 4404f8fdd495SHarald Welte if (spec->hp_mux) 44053d83e577STakashi Iwai via_hp_build(codec); 4406d949cac1SHarald Welte 4407d949cac1SHarald Welte return 1; 4408d949cac1SHarald Welte } 4409d949cac1SHarald Welte 4410d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4411d949cac1SHarald Welte static struct hda_amp_list vt1702_loopbacks[] = { 4412d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 4413d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 4414d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 4415d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 4416d949cac1SHarald Welte { } /* end */ 4417d949cac1SHarald Welte }; 4418d949cac1SHarald Welte #endif 4419d949cac1SHarald Welte 4420d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 4421d949cac1SHarald Welte { 4422d949cac1SHarald Welte struct via_spec *spec; 4423d949cac1SHarald Welte int err; 4424d949cac1SHarald Welte 4425d949cac1SHarald Welte /* create a codec specific record */ 44265b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4427d949cac1SHarald Welte if (spec == NULL) 4428d949cac1SHarald Welte return -ENOMEM; 4429d949cac1SHarald Welte 4430d949cac1SHarald Welte /* automatic parse from the BIOS config */ 4431d949cac1SHarald Welte err = vt1702_parse_auto_config(codec); 4432d949cac1SHarald Welte if (err < 0) { 4433d949cac1SHarald Welte via_free(codec); 4434d949cac1SHarald Welte return err; 4435d949cac1SHarald Welte } else if (!err) { 4436d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 4437d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 4438d949cac1SHarald Welte } 4439d949cac1SHarald Welte 444069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; 444169e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; 4442d949cac1SHarald Welte 4443d949cac1SHarald Welte spec->stream_name_analog = "VT1702 Analog"; 4444d949cac1SHarald Welte spec->stream_analog_playback = &vt1702_pcm_analog_playback; 4445d949cac1SHarald Welte spec->stream_analog_capture = &vt1702_pcm_analog_capture; 4446d949cac1SHarald Welte 4447d949cac1SHarald Welte spec->stream_name_digital = "VT1702 Digital"; 4448d949cac1SHarald Welte spec->stream_digital_playback = &vt1702_pcm_digital_playback; 4449d949cac1SHarald Welte 4450d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 4451d949cac1SHarald Welte spec->adc_nids = vt1702_adc_nids; 4452d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids); 4453337b9d02STakashi Iwai get_mux_nids(codec); 4454d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1702_capture_mixer; 4455d949cac1SHarald Welte spec->num_mixers++; 4456d949cac1SHarald Welte } 4457d949cac1SHarald Welte 4458d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 4459d949cac1SHarald Welte 4460d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 446169e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 4462d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4463d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 4464d949cac1SHarald Welte #endif 4465d949cac1SHarald Welte 4466d949cac1SHarald Welte return 0; 4467d949cac1SHarald Welte } 4468d949cac1SHarald Welte 4469eb7188caSLydia Wang /* Patch for VT1718S */ 4470eb7188caSLydia Wang 4471eb7188caSLydia Wang /* capture mixer elements */ 4472eb7188caSLydia Wang static struct snd_kcontrol_new vt1718S_capture_mixer[] = { 4473eb7188caSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 4474eb7188caSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 4475eb7188caSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 4476eb7188caSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 4477eb7188caSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 4478eb7188caSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 4479eb7188caSLydia Wang HDA_INPUT), 4480eb7188caSLydia Wang { 4481eb7188caSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4482eb7188caSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 4483eb7188caSLydia Wang * So call somewhat different.. 4484eb7188caSLydia Wang */ 4485eb7188caSLydia Wang .name = "Input Source", 4486eb7188caSLydia Wang .count = 2, 4487eb7188caSLydia Wang .info = via_mux_enum_info, 4488eb7188caSLydia Wang .get = via_mux_enum_get, 4489eb7188caSLydia Wang .put = via_mux_enum_put, 4490eb7188caSLydia Wang }, 4491eb7188caSLydia Wang { } /* end */ 4492eb7188caSLydia Wang }; 4493eb7188caSLydia Wang 4494eb7188caSLydia Wang static struct hda_verb vt1718S_volume_init_verbs[] = { 4495eb7188caSLydia Wang /* 4496eb7188caSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4497eb7188caSLydia Wang */ 4498eb7188caSLydia Wang {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4499eb7188caSLydia Wang {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4500eb7188caSLydia Wang 4501eb7188caSLydia Wang 4502eb7188caSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4503eb7188caSLydia Wang * mixer widget 4504eb7188caSLydia Wang */ 4505eb7188caSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4506eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4507eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4508eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4509eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4510eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, 4511eb7188caSLydia Wang 4512eb7188caSLydia Wang /* Setup default input of Front HP to MW9 */ 4513eb7188caSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 4514eb7188caSLydia Wang /* PW9 PW10 Output enable */ 4515eb7188caSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4516eb7188caSLydia Wang {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4517eb7188caSLydia Wang /* PW11 Input enable */ 4518eb7188caSLydia Wang {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, 4519eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 4520eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 4521eb7188caSLydia Wang /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ 4522eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4523eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4524eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4525eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4526eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4527eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4528eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4529eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4530eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4531eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4532eb7188caSLydia Wang /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ 4533eb7188caSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, 4534eb7188caSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, 4535eb7188caSLydia Wang /* Unmute MW4's index 0 */ 4536eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4537eb7188caSLydia Wang { } 4538eb7188caSLydia Wang }; 4539eb7188caSLydia Wang 4540eb7188caSLydia Wang 4541eb7188caSLydia Wang static struct hda_verb vt1718S_uniwill_init_verbs[] = { 4542eb7188caSLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 4543eb7188caSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4544eb7188caSLydia Wang {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4545eb7188caSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4546eb7188caSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4547eb7188caSLydia Wang {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4548eb7188caSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4549eb7188caSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4550eb7188caSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4551eb7188caSLydia Wang { } 4552eb7188caSLydia Wang }; 4553eb7188caSLydia Wang 4554eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_playback = { 4555eb7188caSLydia Wang .substreams = 2, 4556eb7188caSLydia Wang .channels_min = 2, 4557eb7188caSLydia Wang .channels_max = 10, 4558eb7188caSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 4559eb7188caSLydia Wang .ops = { 4560eb7188caSLydia Wang .open = via_playback_pcm_open, 4561eb7188caSLydia Wang .prepare = via_playback_multi_pcm_prepare, 4562eb7188caSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 4563eb7188caSLydia Wang .close = via_pcm_open_close, 4564eb7188caSLydia Wang }, 4565eb7188caSLydia Wang }; 4566eb7188caSLydia Wang 4567eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_capture = { 4568eb7188caSLydia Wang .substreams = 2, 4569eb7188caSLydia Wang .channels_min = 2, 4570eb7188caSLydia Wang .channels_max = 2, 4571eb7188caSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 4572eb7188caSLydia Wang .ops = { 4573eb7188caSLydia Wang .open = via_pcm_open_close, 4574eb7188caSLydia Wang .prepare = via_capture_pcm_prepare, 4575eb7188caSLydia Wang .cleanup = via_capture_pcm_cleanup, 4576eb7188caSLydia Wang .close = via_pcm_open_close, 4577eb7188caSLydia Wang }, 4578eb7188caSLydia Wang }; 4579eb7188caSLydia Wang 4580eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_playback = { 4581eb7188caSLydia Wang .substreams = 2, 4582eb7188caSLydia Wang .channels_min = 2, 4583eb7188caSLydia Wang .channels_max = 2, 4584eb7188caSLydia Wang /* NID is set in via_build_pcms */ 4585eb7188caSLydia Wang .ops = { 4586eb7188caSLydia Wang .open = via_dig_playback_pcm_open, 4587eb7188caSLydia Wang .close = via_dig_playback_pcm_close, 4588eb7188caSLydia Wang .prepare = via_dig_playback_pcm_prepare, 4589eb7188caSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 4590eb7188caSLydia Wang }, 4591eb7188caSLydia Wang }; 4592eb7188caSLydia Wang 4593eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_capture = { 4594eb7188caSLydia Wang .substreams = 1, 4595eb7188caSLydia Wang .channels_min = 2, 4596eb7188caSLydia Wang .channels_max = 2, 4597eb7188caSLydia Wang }; 4598eb7188caSLydia Wang 4599eb7188caSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 4600eb7188caSLydia Wang static int vt1718S_auto_fill_dac_nids(struct via_spec *spec, 4601eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4602eb7188caSLydia Wang { 4603eb7188caSLydia Wang int i; 4604eb7188caSLydia Wang hda_nid_t nid; 4605eb7188caSLydia Wang 4606eb7188caSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 4607eb7188caSLydia Wang 4608eb7188caSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 4609eb7188caSLydia Wang 4610eb7188caSLydia Wang for (i = 0; i < 4; i++) { 4611eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4612eb7188caSLydia Wang if (nid) { 4613eb7188caSLydia Wang /* config dac list */ 4614eb7188caSLydia Wang switch (i) { 4615eb7188caSLydia Wang case AUTO_SEQ_FRONT: 4616eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0x8; 4617eb7188caSLydia Wang break; 4618eb7188caSLydia Wang case AUTO_SEQ_CENLFE: 4619eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0xa; 4620eb7188caSLydia Wang break; 4621eb7188caSLydia Wang case AUTO_SEQ_SURROUND: 4622eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0x9; 4623eb7188caSLydia Wang break; 4624eb7188caSLydia Wang case AUTO_SEQ_SIDE: 4625eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0xb; 4626eb7188caSLydia Wang break; 4627eb7188caSLydia Wang } 4628eb7188caSLydia Wang } 4629eb7188caSLydia Wang } 4630eb7188caSLydia Wang 4631eb7188caSLydia Wang return 0; 4632eb7188caSLydia Wang } 4633eb7188caSLydia Wang 4634eb7188caSLydia Wang /* add playback controls from the parsed DAC table */ 4635eb7188caSLydia Wang static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec, 4636eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4637eb7188caSLydia Wang { 4638eb7188caSLydia Wang char name[32]; 4639ea734963STakashi Iwai static const char * const chname[4] = { 4640ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 4641ea734963STakashi Iwai }; 4642eb7188caSLydia Wang hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb}; 4643eb7188caSLydia Wang hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27}; 4644eb7188caSLydia Wang hda_nid_t nid, nid_vol, nid_mute = 0; 4645eb7188caSLydia Wang int i, err; 4646eb7188caSLydia Wang 4647eb7188caSLydia Wang for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 4648eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4649eb7188caSLydia Wang 4650eb7188caSLydia Wang if (!nid) 4651eb7188caSLydia Wang continue; 4652eb7188caSLydia Wang nid_vol = nid_vols[i]; 4653eb7188caSLydia Wang nid_mute = nid_mutes[i]; 4654eb7188caSLydia Wang 4655eb7188caSLydia Wang if (i == AUTO_SEQ_CENLFE) { 4656eb7188caSLydia Wang /* Center/LFE */ 4657eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4658eb7188caSLydia Wang "Center Playback Volume", 4659eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 4660eb7188caSLydia Wang HDA_OUTPUT)); 4661eb7188caSLydia Wang if (err < 0) 4662eb7188caSLydia Wang return err; 4663eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4664eb7188caSLydia Wang "LFE Playback Volume", 4665eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 4666eb7188caSLydia Wang HDA_OUTPUT)); 4667eb7188caSLydia Wang if (err < 0) 4668eb7188caSLydia Wang return err; 4669eb7188caSLydia Wang err = via_add_control( 4670eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4671eb7188caSLydia Wang "Center Playback Switch", 4672eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 4673eb7188caSLydia Wang HDA_OUTPUT)); 4674eb7188caSLydia Wang if (err < 0) 4675eb7188caSLydia Wang return err; 4676eb7188caSLydia Wang err = via_add_control( 4677eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4678eb7188caSLydia Wang "LFE Playback Switch", 4679eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 4680eb7188caSLydia Wang HDA_OUTPUT)); 4681eb7188caSLydia Wang if (err < 0) 4682eb7188caSLydia Wang return err; 4683eb7188caSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 4684eb7188caSLydia Wang /* Front */ 4685eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4686eb7188caSLydia Wang err = via_add_control( 4687eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4688eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4689eb7188caSLydia Wang if (err < 0) 4690eb7188caSLydia Wang return err; 4691eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4692eb7188caSLydia Wang err = via_add_control( 4693eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4694eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4695eb7188caSLydia Wang HDA_OUTPUT)); 4696eb7188caSLydia Wang if (err < 0) 4697eb7188caSLydia Wang return err; 4698eb7188caSLydia Wang } else { 4699eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4700eb7188caSLydia Wang err = via_add_control( 4701eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4702eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4703eb7188caSLydia Wang if (err < 0) 4704eb7188caSLydia Wang return err; 4705eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4706eb7188caSLydia Wang err = via_add_control( 4707eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4708eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4709eb7188caSLydia Wang HDA_OUTPUT)); 4710eb7188caSLydia Wang if (err < 0) 4711eb7188caSLydia Wang return err; 4712eb7188caSLydia Wang } 4713eb7188caSLydia Wang } 4714eb7188caSLydia Wang return 0; 4715eb7188caSLydia Wang } 4716eb7188caSLydia Wang 4717eb7188caSLydia Wang static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4718eb7188caSLydia Wang { 4719eb7188caSLydia Wang int err; 4720eb7188caSLydia Wang 4721eb7188caSLydia Wang if (!pin) 4722eb7188caSLydia Wang return 0; 4723eb7188caSLydia Wang 4724eb7188caSLydia Wang spec->multiout.hp_nid = 0xc; /* AOW4 */ 4725eb7188caSLydia Wang spec->hp_independent_mode_index = 1; 4726eb7188caSLydia Wang 4727eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4728eb7188caSLydia Wang "Headphone Playback Volume", 4729eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT)); 4730eb7188caSLydia Wang if (err < 0) 4731eb7188caSLydia Wang return err; 4732eb7188caSLydia Wang 4733eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4734eb7188caSLydia Wang "Headphone Playback Switch", 4735eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4736eb7188caSLydia Wang if (err < 0) 4737eb7188caSLydia Wang return err; 4738eb7188caSLydia Wang 4739eb7188caSLydia Wang create_hp_imux(spec); 4740eb7188caSLydia Wang return 0; 4741eb7188caSLydia Wang } 4742eb7188caSLydia Wang 4743eb7188caSLydia Wang /* create playback/capture controls for input pins */ 474410a20af7STakashi Iwai static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec, 4745eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4746eb7188caSLydia Wang { 4747f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff }; 474810a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, 4749f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 4750eb7188caSLydia Wang } 4751eb7188caSLydia Wang 4752eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec) 4753eb7188caSLydia Wang { 4754eb7188caSLydia Wang struct via_spec *spec = codec->spec; 4755eb7188caSLydia Wang int err; 4756eb7188caSLydia Wang 4757eb7188caSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4758eb7188caSLydia Wang 4759eb7188caSLydia Wang if (err < 0) 4760eb7188caSLydia Wang return err; 4761eb7188caSLydia Wang err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg); 4762eb7188caSLydia Wang if (err < 0) 4763eb7188caSLydia Wang return err; 4764eb7188caSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4765eb7188caSLydia Wang return 0; /* can't find valid BIOS pin config */ 4766eb7188caSLydia Wang 4767eb7188caSLydia Wang err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg); 4768eb7188caSLydia Wang if (err < 0) 4769eb7188caSLydia Wang return err; 4770eb7188caSLydia Wang err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4771eb7188caSLydia Wang if (err < 0) 4772eb7188caSLydia Wang return err; 477310a20af7STakashi Iwai err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg); 4774eb7188caSLydia Wang if (err < 0) 4775eb7188caSLydia Wang return err; 4776eb7188caSLydia Wang 4777eb7188caSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4778eb7188caSLydia Wang 4779eb7188caSLydia Wang fill_dig_outs(codec); 4780eb7188caSLydia Wang 4781eb7188caSLydia Wang if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) 4782eb7188caSLydia Wang spec->dig_in_nid = 0x13; 4783eb7188caSLydia Wang 4784eb7188caSLydia Wang if (spec->kctls.list) 4785eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 4786eb7188caSLydia Wang 4787eb7188caSLydia Wang spec->input_mux = &spec->private_imux[0]; 4788eb7188caSLydia Wang 4789eb7188caSLydia Wang if (spec->hp_mux) 47903d83e577STakashi Iwai via_hp_build(codec); 4791eb7188caSLydia Wang 47925b0cb1d8SJaroslav Kysela via_smart51_build(spec); 4793eb7188caSLydia Wang 4794eb7188caSLydia Wang return 1; 4795eb7188caSLydia Wang } 4796eb7188caSLydia Wang 4797eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4798eb7188caSLydia Wang static struct hda_amp_list vt1718S_loopbacks[] = { 4799eb7188caSLydia Wang { 0x21, HDA_INPUT, 1 }, 4800eb7188caSLydia Wang { 0x21, HDA_INPUT, 2 }, 4801eb7188caSLydia Wang { 0x21, HDA_INPUT, 3 }, 4802eb7188caSLydia Wang { 0x21, HDA_INPUT, 4 }, 4803eb7188caSLydia Wang { } /* end */ 4804eb7188caSLydia Wang }; 4805eb7188caSLydia Wang #endif 4806eb7188caSLydia Wang 4807eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 4808eb7188caSLydia Wang { 4809eb7188caSLydia Wang struct via_spec *spec; 4810eb7188caSLydia Wang int err; 4811eb7188caSLydia Wang 4812eb7188caSLydia Wang /* create a codec specific record */ 48135b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4814eb7188caSLydia Wang if (spec == NULL) 4815eb7188caSLydia Wang return -ENOMEM; 4816eb7188caSLydia Wang 4817eb7188caSLydia Wang /* automatic parse from the BIOS config */ 4818eb7188caSLydia Wang err = vt1718S_parse_auto_config(codec); 4819eb7188caSLydia Wang if (err < 0) { 4820eb7188caSLydia Wang via_free(codec); 4821eb7188caSLydia Wang return err; 4822eb7188caSLydia Wang } else if (!err) { 4823eb7188caSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 4824eb7188caSLydia Wang "from BIOS. Using genenic mode...\n"); 4825eb7188caSLydia Wang } 4826eb7188caSLydia Wang 4827eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; 4828eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; 4829eb7188caSLydia Wang 4830bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060441) 4831bb3c6bfcSLydia Wang spec->stream_name_analog = "VT2020 Analog"; 4832bb3c6bfcSLydia Wang else if (codec->vendor_id == 0x11064441) 4833bb3c6bfcSLydia Wang spec->stream_name_analog = "VT1828S Analog"; 4834bb3c6bfcSLydia Wang else 4835eb7188caSLydia Wang spec->stream_name_analog = "VT1718S Analog"; 4836eb7188caSLydia Wang spec->stream_analog_playback = &vt1718S_pcm_analog_playback; 4837eb7188caSLydia Wang spec->stream_analog_capture = &vt1718S_pcm_analog_capture; 4838eb7188caSLydia Wang 4839bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060441) 4840bb3c6bfcSLydia Wang spec->stream_name_digital = "VT2020 Digital"; 4841bb3c6bfcSLydia Wang else if (codec->vendor_id == 0x11064441) 4842bb3c6bfcSLydia Wang spec->stream_name_digital = "VT1828S Digital"; 4843bb3c6bfcSLydia Wang else 4844eb7188caSLydia Wang spec->stream_name_digital = "VT1718S Digital"; 4845eb7188caSLydia Wang spec->stream_digital_playback = &vt1718S_pcm_digital_playback; 4846bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) 4847eb7188caSLydia Wang spec->stream_digital_capture = &vt1718S_pcm_digital_capture; 4848eb7188caSLydia Wang 4849eb7188caSLydia Wang if (!spec->adc_nids && spec->input_mux) { 4850eb7188caSLydia Wang spec->adc_nids = vt1718S_adc_nids; 4851eb7188caSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids); 4852eb7188caSLydia Wang get_mux_nids(codec); 4853bb3c6bfcSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 4854bb3c6bfcSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 4855eb7188caSLydia Wang spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; 4856eb7188caSLydia Wang spec->num_mixers++; 4857eb7188caSLydia Wang } 4858eb7188caSLydia Wang 4859eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 4860eb7188caSLydia Wang 4861eb7188caSLydia Wang codec->patch_ops.init = via_auto_init; 48620f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 4863eb7188caSLydia Wang 4864eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4865eb7188caSLydia Wang spec->loopback.amplist = vt1718S_loopbacks; 4866eb7188caSLydia Wang #endif 4867eb7188caSLydia Wang 4868eb7188caSLydia Wang return 0; 4869eb7188caSLydia Wang } 4870f3db423dSLydia Wang 4871f3db423dSLydia Wang /* Patch for VT1716S */ 4872f3db423dSLydia Wang 4873f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 4874f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 4875f3db423dSLydia Wang { 4876f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 4877f3db423dSLydia Wang uinfo->count = 1; 4878f3db423dSLydia Wang uinfo->value.integer.min = 0; 4879f3db423dSLydia Wang uinfo->value.integer.max = 1; 4880f3db423dSLydia Wang return 0; 4881f3db423dSLydia Wang } 4882f3db423dSLydia Wang 4883f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 4884f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 4885f3db423dSLydia Wang { 4886f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4887f3db423dSLydia Wang int index = 0; 4888f3db423dSLydia Wang 4889f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 4890f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 4891f3db423dSLydia Wang if (index != -1) 4892f3db423dSLydia Wang *ucontrol->value.integer.value = index; 4893f3db423dSLydia Wang 4894f3db423dSLydia Wang return 0; 4895f3db423dSLydia Wang } 4896f3db423dSLydia Wang 4897f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 4898f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 4899f3db423dSLydia Wang { 4900f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4901f3db423dSLydia Wang struct via_spec *spec = codec->spec; 4902f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 4903f3db423dSLydia Wang 4904f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 4905f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 4906f3db423dSLydia Wang spec->dmic_enabled = index; 4907f3db423dSLydia Wang set_jack_power_state(codec); 4908f3db423dSLydia Wang 4909f3db423dSLydia Wang return 1; 4910f3db423dSLydia Wang } 4911f3db423dSLydia Wang 4912f3db423dSLydia Wang /* capture mixer elements */ 4913f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_capture_mixer[] = { 4914f3db423dSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 4915f3db423dSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 4916f3db423dSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 4917f3db423dSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 4918f3db423dSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 4919f3db423dSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 4920f3db423dSLydia Wang HDA_INPUT), 4921f3db423dSLydia Wang { 4922f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4923f3db423dSLydia Wang .name = "Input Source", 4924f3db423dSLydia Wang .count = 1, 4925f3db423dSLydia Wang .info = via_mux_enum_info, 4926f3db423dSLydia Wang .get = via_mux_enum_get, 4927f3db423dSLydia Wang .put = via_mux_enum_put, 4928f3db423dSLydia Wang }, 4929f3db423dSLydia Wang { } /* end */ 4930f3db423dSLydia Wang }; 4931f3db423dSLydia Wang 4932f3db423dSLydia Wang static struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 4933f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 4934f3db423dSLydia Wang { 4935f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4936f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 49375b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 4938f3db423dSLydia Wang .count = 1, 4939f3db423dSLydia Wang .info = vt1716s_dmic_info, 4940f3db423dSLydia Wang .get = vt1716s_dmic_get, 4941f3db423dSLydia Wang .put = vt1716s_dmic_put, 4942f3db423dSLydia Wang }, 4943f3db423dSLydia Wang {} /* end */ 4944f3db423dSLydia Wang }; 4945f3db423dSLydia Wang 4946f3db423dSLydia Wang 4947f3db423dSLydia Wang /* mono-out mixer elements */ 4948f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 4949f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 4950f3db423dSLydia Wang { } /* end */ 4951f3db423dSLydia Wang }; 4952f3db423dSLydia Wang 4953f3db423dSLydia Wang static struct hda_verb vt1716S_volume_init_verbs[] = { 4954f3db423dSLydia Wang /* 4955f3db423dSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4956f3db423dSLydia Wang */ 4957f3db423dSLydia Wang {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4958f3db423dSLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4959f3db423dSLydia Wang 4960f3db423dSLydia Wang 4961f3db423dSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4962f3db423dSLydia Wang * mixer widget 4963f3db423dSLydia Wang */ 4964f3db423dSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4965f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4966f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4967f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4968f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4969f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4970f3db423dSLydia Wang 4971f3db423dSLydia Wang /* MUX Indices: Stereo Mixer = 5 */ 4972f3db423dSLydia Wang {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, 4973f3db423dSLydia Wang 4974f3db423dSLydia Wang /* Setup default input of PW4 to MW0 */ 4975f3db423dSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 4976f3db423dSLydia Wang 4977f3db423dSLydia Wang /* Setup default input of SW1 as MW0 */ 4978f3db423dSLydia Wang {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, 4979f3db423dSLydia Wang 4980f3db423dSLydia Wang /* Setup default input of SW4 as AOW0 */ 4981f3db423dSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 4982f3db423dSLydia Wang 4983f3db423dSLydia Wang /* PW9 PW10 Output enable */ 4984f3db423dSLydia Wang {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4985f3db423dSLydia Wang {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4986f3db423dSLydia Wang 4987f3db423dSLydia Wang /* Unmute SW1, PW12 */ 4988f3db423dSLydia Wang {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4989f3db423dSLydia Wang {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 4990f3db423dSLydia Wang /* PW12 Output enable */ 4991f3db423dSLydia Wang {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4992f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 4993f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 4994f3db423dSLydia Wang /* don't bybass mixer */ 4995f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 4996f3db423dSLydia Wang /* Enable mono output */ 4997f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 4998f3db423dSLydia Wang { } 4999f3db423dSLydia Wang }; 5000f3db423dSLydia Wang 5001f3db423dSLydia Wang 5002f3db423dSLydia Wang static struct hda_verb vt1716S_uniwill_init_verbs[] = { 5003f3db423dSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 5004f3db423dSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 5005f3db423dSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5006f3db423dSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5007f3db423dSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5008f3db423dSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 5009f3db423dSLydia Wang AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, 5010f3db423dSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5011f3db423dSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5012f3db423dSLydia Wang { } 5013f3db423dSLydia Wang }; 5014f3db423dSLydia Wang 5015f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_playback = { 5016f3db423dSLydia Wang .substreams = 2, 5017f3db423dSLydia Wang .channels_min = 2, 5018f3db423dSLydia Wang .channels_max = 6, 5019f3db423dSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 5020f3db423dSLydia Wang .ops = { 5021f3db423dSLydia Wang .open = via_playback_pcm_open, 5022f3db423dSLydia Wang .prepare = via_playback_multi_pcm_prepare, 5023f3db423dSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 5024f3db423dSLydia Wang .close = via_pcm_open_close, 5025f3db423dSLydia Wang }, 5026f3db423dSLydia Wang }; 5027f3db423dSLydia Wang 5028f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_capture = { 5029f3db423dSLydia Wang .substreams = 2, 5030f3db423dSLydia Wang .channels_min = 2, 5031f3db423dSLydia Wang .channels_max = 2, 5032f3db423dSLydia Wang .nid = 0x13, /* NID to query formats and rates */ 5033f3db423dSLydia Wang .ops = { 5034f3db423dSLydia Wang .open = via_pcm_open_close, 5035f3db423dSLydia Wang .prepare = via_capture_pcm_prepare, 5036f3db423dSLydia Wang .cleanup = via_capture_pcm_cleanup, 5037f3db423dSLydia Wang .close = via_pcm_open_close, 5038f3db423dSLydia Wang }, 5039f3db423dSLydia Wang }; 5040f3db423dSLydia Wang 5041f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_digital_playback = { 5042f3db423dSLydia Wang .substreams = 2, 5043f3db423dSLydia Wang .channels_min = 2, 5044f3db423dSLydia Wang .channels_max = 2, 5045f3db423dSLydia Wang /* NID is set in via_build_pcms */ 5046f3db423dSLydia Wang .ops = { 5047f3db423dSLydia Wang .open = via_dig_playback_pcm_open, 5048f3db423dSLydia Wang .close = via_dig_playback_pcm_close, 5049f3db423dSLydia Wang .prepare = via_dig_playback_pcm_prepare, 5050f3db423dSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 5051f3db423dSLydia Wang }, 5052f3db423dSLydia Wang }; 5053f3db423dSLydia Wang 5054f3db423dSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 5055f3db423dSLydia Wang static int vt1716S_auto_fill_dac_nids(struct via_spec *spec, 5056f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5057f3db423dSLydia Wang { int i; 5058f3db423dSLydia Wang hda_nid_t nid; 5059f3db423dSLydia Wang 5060f3db423dSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 5061f3db423dSLydia Wang 5062f3db423dSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 5063f3db423dSLydia Wang 5064f3db423dSLydia Wang for (i = 0; i < 3; i++) { 5065f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 5066f3db423dSLydia Wang if (nid) { 5067f3db423dSLydia Wang /* config dac list */ 5068f3db423dSLydia Wang switch (i) { 5069f3db423dSLydia Wang case AUTO_SEQ_FRONT: 5070f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x10; 5071f3db423dSLydia Wang break; 5072f3db423dSLydia Wang case AUTO_SEQ_CENLFE: 5073f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x25; 5074f3db423dSLydia Wang break; 5075f3db423dSLydia Wang case AUTO_SEQ_SURROUND: 5076f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x11; 5077f3db423dSLydia Wang break; 5078f3db423dSLydia Wang } 5079f3db423dSLydia Wang } 5080f3db423dSLydia Wang } 5081f3db423dSLydia Wang 5082f3db423dSLydia Wang return 0; 5083f3db423dSLydia Wang } 5084f3db423dSLydia Wang 5085f3db423dSLydia Wang /* add playback controls from the parsed DAC table */ 5086f3db423dSLydia Wang static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec, 5087f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5088f3db423dSLydia Wang { 5089f3db423dSLydia Wang char name[32]; 5090ea734963STakashi Iwai static const char * const chname[3] = { 5091ea734963STakashi Iwai "Front", "Surround", "C/LFE" 5092ea734963STakashi Iwai }; 5093f3db423dSLydia Wang hda_nid_t nid_vols[] = {0x10, 0x11, 0x25}; 5094f3db423dSLydia Wang hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27}; 5095f3db423dSLydia Wang hda_nid_t nid, nid_vol, nid_mute; 5096f3db423dSLydia Wang int i, err; 5097f3db423dSLydia Wang 5098f3db423dSLydia Wang for (i = 0; i <= AUTO_SEQ_CENLFE; i++) { 5099f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 5100f3db423dSLydia Wang 5101f3db423dSLydia Wang if (!nid) 5102f3db423dSLydia Wang continue; 5103f3db423dSLydia Wang 5104f3db423dSLydia Wang nid_vol = nid_vols[i]; 5105f3db423dSLydia Wang nid_mute = nid_mutes[i]; 5106f3db423dSLydia Wang 5107f3db423dSLydia Wang if (i == AUTO_SEQ_CENLFE) { 5108f3db423dSLydia Wang err = via_add_control( 5109f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 5110f3db423dSLydia Wang "Center Playback Volume", 5111f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); 5112f3db423dSLydia Wang if (err < 0) 5113f3db423dSLydia Wang return err; 5114f3db423dSLydia Wang err = via_add_control( 5115f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 5116f3db423dSLydia Wang "LFE Playback Volume", 5117f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); 5118f3db423dSLydia Wang if (err < 0) 5119f3db423dSLydia Wang return err; 5120f3db423dSLydia Wang err = via_add_control( 5121f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 5122f3db423dSLydia Wang "Center Playback Switch", 5123f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 5124f3db423dSLydia Wang HDA_OUTPUT)); 5125f3db423dSLydia Wang if (err < 0) 5126f3db423dSLydia Wang return err; 5127f3db423dSLydia Wang err = via_add_control( 5128f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 5129f3db423dSLydia Wang "LFE Playback Switch", 5130f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 5131f3db423dSLydia Wang HDA_OUTPUT)); 5132f3db423dSLydia Wang if (err < 0) 5133f3db423dSLydia Wang return err; 5134f3db423dSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 5135f3db423dSLydia Wang 5136f3db423dSLydia Wang err = via_add_control( 5137f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 5138f3db423dSLydia Wang "Master Front Playback Volume", 5139f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 5140f3db423dSLydia Wang if (err < 0) 5141f3db423dSLydia Wang return err; 5142f3db423dSLydia Wang err = via_add_control( 5143f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 5144f3db423dSLydia Wang "Master Front Playback Switch", 5145f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 5146f3db423dSLydia Wang if (err < 0) 5147f3db423dSLydia Wang return err; 5148f3db423dSLydia Wang 5149f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 5150f3db423dSLydia Wang err = via_add_control( 5151f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 5152f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 5153f3db423dSLydia Wang if (err < 0) 5154f3db423dSLydia Wang return err; 5155f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 5156f3db423dSLydia Wang err = via_add_control( 5157f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 5158f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 5159f3db423dSLydia Wang HDA_OUTPUT)); 5160f3db423dSLydia Wang if (err < 0) 5161f3db423dSLydia Wang return err; 5162f3db423dSLydia Wang } else { 5163f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 5164f3db423dSLydia Wang err = via_add_control( 5165f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 5166f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 5167f3db423dSLydia Wang if (err < 0) 5168f3db423dSLydia Wang return err; 5169f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 5170f3db423dSLydia Wang err = via_add_control( 5171f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 5172f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 5173f3db423dSLydia Wang HDA_OUTPUT)); 5174f3db423dSLydia Wang if (err < 0) 5175f3db423dSLydia Wang return err; 5176f3db423dSLydia Wang } 5177f3db423dSLydia Wang } 5178f3db423dSLydia Wang return 0; 5179f3db423dSLydia Wang } 5180f3db423dSLydia Wang 5181f3db423dSLydia Wang static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 5182f3db423dSLydia Wang { 5183f3db423dSLydia Wang int err; 5184f3db423dSLydia Wang 5185f3db423dSLydia Wang if (!pin) 5186f3db423dSLydia Wang return 0; 5187f3db423dSLydia Wang 5188f3db423dSLydia Wang spec->multiout.hp_nid = 0x25; /* AOW3 */ 5189f3db423dSLydia Wang spec->hp_independent_mode_index = 1; 5190f3db423dSLydia Wang 5191f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 5192f3db423dSLydia Wang "Headphone Playback Volume", 5193f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 5194f3db423dSLydia Wang if (err < 0) 5195f3db423dSLydia Wang return err; 5196f3db423dSLydia Wang 5197f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 5198f3db423dSLydia Wang "Headphone Playback Switch", 5199f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 5200f3db423dSLydia Wang if (err < 0) 5201f3db423dSLydia Wang return err; 5202f3db423dSLydia Wang 5203f3db423dSLydia Wang create_hp_imux(spec); 5204f3db423dSLydia Wang return 0; 5205f3db423dSLydia Wang } 5206f3db423dSLydia Wang 5207f3db423dSLydia Wang /* create playback/capture controls for input pins */ 520810a20af7STakashi Iwai static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec, 5209f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5210f3db423dSLydia Wang { 5211f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; 521210a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, 5213f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 5214f3db423dSLydia Wang } 5215f3db423dSLydia Wang 5216f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec) 5217f3db423dSLydia Wang { 5218f3db423dSLydia Wang struct via_spec *spec = codec->spec; 5219f3db423dSLydia Wang int err; 5220f3db423dSLydia Wang 5221f3db423dSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 5222f3db423dSLydia Wang if (err < 0) 5223f3db423dSLydia Wang return err; 5224f3db423dSLydia Wang err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg); 5225f3db423dSLydia Wang if (err < 0) 5226f3db423dSLydia Wang return err; 5227f3db423dSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 5228f3db423dSLydia Wang return 0; /* can't find valid BIOS pin config */ 5229f3db423dSLydia Wang 5230f3db423dSLydia Wang err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg); 5231f3db423dSLydia Wang if (err < 0) 5232f3db423dSLydia Wang return err; 5233f3db423dSLydia Wang err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 5234f3db423dSLydia Wang if (err < 0) 5235f3db423dSLydia Wang return err; 523610a20af7STakashi Iwai err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg); 5237f3db423dSLydia Wang if (err < 0) 5238f3db423dSLydia Wang return err; 5239f3db423dSLydia Wang 5240f3db423dSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 5241f3db423dSLydia Wang 5242f3db423dSLydia Wang fill_dig_outs(codec); 5243f3db423dSLydia Wang 5244f3db423dSLydia Wang if (spec->kctls.list) 5245f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 5246f3db423dSLydia Wang 5247f3db423dSLydia Wang spec->input_mux = &spec->private_imux[0]; 5248f3db423dSLydia Wang 5249f3db423dSLydia Wang if (spec->hp_mux) 52503d83e577STakashi Iwai via_hp_build(codec); 5251f3db423dSLydia Wang 52525b0cb1d8SJaroslav Kysela via_smart51_build(spec); 5253f3db423dSLydia Wang 5254f3db423dSLydia Wang return 1; 5255f3db423dSLydia Wang } 5256f3db423dSLydia Wang 5257f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5258f3db423dSLydia Wang static struct hda_amp_list vt1716S_loopbacks[] = { 5259f3db423dSLydia Wang { 0x16, HDA_INPUT, 1 }, 5260f3db423dSLydia Wang { 0x16, HDA_INPUT, 2 }, 5261f3db423dSLydia Wang { 0x16, HDA_INPUT, 3 }, 5262f3db423dSLydia Wang { 0x16, HDA_INPUT, 4 }, 5263f3db423dSLydia Wang { } /* end */ 5264f3db423dSLydia Wang }; 5265f3db423dSLydia Wang #endif 5266f3db423dSLydia Wang 5267f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 5268f3db423dSLydia Wang { 5269f3db423dSLydia Wang struct via_spec *spec; 5270f3db423dSLydia Wang int err; 5271f3db423dSLydia Wang 5272f3db423dSLydia Wang /* create a codec specific record */ 52735b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 5274f3db423dSLydia Wang if (spec == NULL) 5275f3db423dSLydia Wang return -ENOMEM; 5276f3db423dSLydia Wang 5277f3db423dSLydia Wang /* automatic parse from the BIOS config */ 5278f3db423dSLydia Wang err = vt1716S_parse_auto_config(codec); 5279f3db423dSLydia Wang if (err < 0) { 5280f3db423dSLydia Wang via_free(codec); 5281f3db423dSLydia Wang return err; 5282f3db423dSLydia Wang } else if (!err) { 5283f3db423dSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 5284f3db423dSLydia Wang "from BIOS. Using genenic mode...\n"); 5285f3db423dSLydia Wang } 5286f3db423dSLydia Wang 5287f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; 5288f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; 5289f3db423dSLydia Wang 5290f3db423dSLydia Wang spec->stream_name_analog = "VT1716S Analog"; 5291f3db423dSLydia Wang spec->stream_analog_playback = &vt1716S_pcm_analog_playback; 5292f3db423dSLydia Wang spec->stream_analog_capture = &vt1716S_pcm_analog_capture; 5293f3db423dSLydia Wang 5294f3db423dSLydia Wang spec->stream_name_digital = "VT1716S Digital"; 5295f3db423dSLydia Wang spec->stream_digital_playback = &vt1716S_pcm_digital_playback; 5296f3db423dSLydia Wang 5297f3db423dSLydia Wang if (!spec->adc_nids && spec->input_mux) { 5298f3db423dSLydia Wang spec->adc_nids = vt1716S_adc_nids; 5299f3db423dSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids); 5300f3db423dSLydia Wang get_mux_nids(codec); 5301f3db423dSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 5302f3db423dSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 5303f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; 5304f3db423dSLydia Wang spec->num_mixers++; 5305f3db423dSLydia Wang } 5306f3db423dSLydia Wang 5307f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 5308f3db423dSLydia Wang spec->num_mixers++; 5309f3db423dSLydia Wang 5310f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 5311f3db423dSLydia Wang 5312f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 5313f3db423dSLydia Wang 5314f3db423dSLydia Wang codec->patch_ops.init = via_auto_init; 53150f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 5316f3db423dSLydia Wang 5317f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5318f3db423dSLydia Wang spec->loopback.amplist = vt1716S_loopbacks; 5319f3db423dSLydia Wang #endif 5320f3db423dSLydia Wang 5321f3db423dSLydia Wang return 0; 5322f3db423dSLydia Wang } 532325eaba2fSLydia Wang 532425eaba2fSLydia Wang /* for vt2002P */ 532525eaba2fSLydia Wang 532625eaba2fSLydia Wang /* capture mixer elements */ 532725eaba2fSLydia Wang static struct snd_kcontrol_new vt2002P_capture_mixer[] = { 532825eaba2fSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 532925eaba2fSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 533025eaba2fSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 533125eaba2fSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 533225eaba2fSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 533325eaba2fSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 533425eaba2fSLydia Wang HDA_INPUT), 533525eaba2fSLydia Wang { 533625eaba2fSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 533725eaba2fSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 533825eaba2fSLydia Wang * So call somewhat different.. 533925eaba2fSLydia Wang */ 534025eaba2fSLydia Wang /* .name = "Capture Source", */ 534125eaba2fSLydia Wang .name = "Input Source", 534225eaba2fSLydia Wang .count = 2, 534325eaba2fSLydia Wang .info = via_mux_enum_info, 534425eaba2fSLydia Wang .get = via_mux_enum_get, 534525eaba2fSLydia Wang .put = via_mux_enum_put, 534625eaba2fSLydia Wang }, 534725eaba2fSLydia Wang { } /* end */ 534825eaba2fSLydia Wang }; 534925eaba2fSLydia Wang 535025eaba2fSLydia Wang static struct hda_verb vt2002P_volume_init_verbs[] = { 535125eaba2fSLydia Wang /* 535225eaba2fSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 535325eaba2fSLydia Wang */ 535425eaba2fSLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 535525eaba2fSLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 535625eaba2fSLydia Wang 535725eaba2fSLydia Wang 535825eaba2fSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 535925eaba2fSLydia Wang * mixer widget 536025eaba2fSLydia Wang */ 536125eaba2fSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 536225eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 536325eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 536425eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 536525eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 536625eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 536725eaba2fSLydia Wang 536825eaba2fSLydia Wang /* MUX Indices: Mic = 0 */ 536925eaba2fSLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 537025eaba2fSLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 537125eaba2fSLydia Wang 537225eaba2fSLydia Wang /* PW9 Output enable */ 537325eaba2fSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 537425eaba2fSLydia Wang 537525eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 537625eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 537725eaba2fSLydia Wang 537825eaba2fSLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 537925eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 538025eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 538125eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 538225eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 538325eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 538425eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 538525eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 538625eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 538725eaba2fSLydia Wang 538825eaba2fSLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 538925eaba2fSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 539025eaba2fSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 539125eaba2fSLydia Wang {0x37, AC_VERB_SET_CONNECT_SEL, 0}, 539225eaba2fSLydia Wang {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, 539325eaba2fSLydia Wang 539425eaba2fSLydia Wang /* set PW0 index=0 (MW0) */ 539525eaba2fSLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 539625eaba2fSLydia Wang 539725eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 539825eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 539925eaba2fSLydia Wang { } 540025eaba2fSLydia Wang }; 540125eaba2fSLydia Wang 540225eaba2fSLydia Wang 540325eaba2fSLydia Wang static struct hda_verb vt2002P_uniwill_init_verbs[] = { 540425eaba2fSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 540525eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 540625eaba2fSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, 540725eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 540825eaba2fSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 540925eaba2fSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 541025eaba2fSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 541125eaba2fSLydia Wang { } 541225eaba2fSLydia Wang }; 541325eaba2fSLydia Wang 541425eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_playback = { 541525eaba2fSLydia Wang .substreams = 2, 541625eaba2fSLydia Wang .channels_min = 2, 541725eaba2fSLydia Wang .channels_max = 2, 541825eaba2fSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 541925eaba2fSLydia Wang .ops = { 542025eaba2fSLydia Wang .open = via_playback_pcm_open, 542125eaba2fSLydia Wang .prepare = via_playback_multi_pcm_prepare, 542225eaba2fSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 542325eaba2fSLydia Wang .close = via_pcm_open_close, 542425eaba2fSLydia Wang }, 542525eaba2fSLydia Wang }; 542625eaba2fSLydia Wang 542725eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_capture = { 542825eaba2fSLydia Wang .substreams = 2, 542925eaba2fSLydia Wang .channels_min = 2, 543025eaba2fSLydia Wang .channels_max = 2, 543125eaba2fSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 543225eaba2fSLydia Wang .ops = { 543325eaba2fSLydia Wang .open = via_pcm_open_close, 543425eaba2fSLydia Wang .prepare = via_capture_pcm_prepare, 543525eaba2fSLydia Wang .cleanup = via_capture_pcm_cleanup, 543625eaba2fSLydia Wang .close = via_pcm_open_close, 543725eaba2fSLydia Wang }, 543825eaba2fSLydia Wang }; 543925eaba2fSLydia Wang 544025eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_digital_playback = { 544125eaba2fSLydia Wang .substreams = 1, 544225eaba2fSLydia Wang .channels_min = 2, 544325eaba2fSLydia Wang .channels_max = 2, 544425eaba2fSLydia Wang /* NID is set in via_build_pcms */ 544525eaba2fSLydia Wang .ops = { 544625eaba2fSLydia Wang .open = via_dig_playback_pcm_open, 544725eaba2fSLydia Wang .close = via_dig_playback_pcm_close, 544825eaba2fSLydia Wang .prepare = via_dig_playback_pcm_prepare, 544925eaba2fSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 545025eaba2fSLydia Wang }, 545125eaba2fSLydia Wang }; 545225eaba2fSLydia Wang 545325eaba2fSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 545425eaba2fSLydia Wang static int vt2002P_auto_fill_dac_nids(struct via_spec *spec, 545525eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 545625eaba2fSLydia Wang { 545725eaba2fSLydia Wang spec->multiout.num_dacs = 1; 545825eaba2fSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 545925eaba2fSLydia Wang if (cfg->line_out_pins[0]) 546025eaba2fSLydia Wang spec->multiout.dac_nids[0] = 0x8; 546125eaba2fSLydia Wang return 0; 546225eaba2fSLydia Wang } 546325eaba2fSLydia Wang 546425eaba2fSLydia Wang /* add playback controls from the parsed DAC table */ 546525eaba2fSLydia Wang static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec, 546625eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 546725eaba2fSLydia Wang { 546825eaba2fSLydia Wang int err; 546925eaba2fSLydia Wang 547025eaba2fSLydia Wang if (!cfg->line_out_pins[0]) 547125eaba2fSLydia Wang return -1; 547225eaba2fSLydia Wang 547325eaba2fSLydia Wang 547425eaba2fSLydia Wang /* Line-Out: PortE */ 547525eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 547625eaba2fSLydia Wang "Master Front Playback Volume", 547725eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); 547825eaba2fSLydia Wang if (err < 0) 547925eaba2fSLydia Wang return err; 548025eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, 548125eaba2fSLydia Wang "Master Front Playback Switch", 548225eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT)); 548325eaba2fSLydia Wang if (err < 0) 548425eaba2fSLydia Wang return err; 548525eaba2fSLydia Wang 548625eaba2fSLydia Wang return 0; 548725eaba2fSLydia Wang } 548825eaba2fSLydia Wang 548925eaba2fSLydia Wang static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 549025eaba2fSLydia Wang { 549125eaba2fSLydia Wang int err; 549225eaba2fSLydia Wang 549325eaba2fSLydia Wang if (!pin) 549425eaba2fSLydia Wang return 0; 549525eaba2fSLydia Wang 549625eaba2fSLydia Wang spec->multiout.hp_nid = 0x9; 549725eaba2fSLydia Wang spec->hp_independent_mode_index = 1; 549825eaba2fSLydia Wang 549925eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 550025eaba2fSLydia Wang "Headphone Playback Volume", 550125eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL( 550225eaba2fSLydia Wang spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); 550325eaba2fSLydia Wang if (err < 0) 550425eaba2fSLydia Wang return err; 550525eaba2fSLydia Wang 550625eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 550725eaba2fSLydia Wang "Headphone Playback Switch", 550825eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 550925eaba2fSLydia Wang if (err < 0) 551025eaba2fSLydia Wang return err; 551125eaba2fSLydia Wang 551225eaba2fSLydia Wang create_hp_imux(spec); 551325eaba2fSLydia Wang return 0; 551425eaba2fSLydia Wang } 551525eaba2fSLydia Wang 551625eaba2fSLydia Wang /* create playback/capture controls for input pins */ 551710a20af7STakashi Iwai static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec, 551825eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 551925eaba2fSLydia Wang { 552010a20af7STakashi Iwai struct via_spec *spec = codec->spec; 552125eaba2fSLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 5522f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff }; 5523f3268512STakashi Iwai int err; 552425eaba2fSLydia Wang 552510a20af7STakashi Iwai err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, 5526f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 552725eaba2fSLydia Wang if (err < 0) 552825eaba2fSLydia Wang return err; 552925eaba2fSLydia Wang /* build volume/mute control of loopback */ 55307b315bb4STakashi Iwai err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21); 553125eaba2fSLydia Wang if (err < 0) 553225eaba2fSLydia Wang return err; 553325eaba2fSLydia Wang 553425eaba2fSLydia Wang /* for digital mic select */ 553510a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL); 553625eaba2fSLydia Wang 553725eaba2fSLydia Wang return 0; 553825eaba2fSLydia Wang } 553925eaba2fSLydia Wang 554025eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec) 554125eaba2fSLydia Wang { 554225eaba2fSLydia Wang struct via_spec *spec = codec->spec; 554325eaba2fSLydia Wang int err; 554425eaba2fSLydia Wang 554525eaba2fSLydia Wang 554625eaba2fSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 554725eaba2fSLydia Wang if (err < 0) 554825eaba2fSLydia Wang return err; 554925eaba2fSLydia Wang 555025eaba2fSLydia Wang err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg); 555125eaba2fSLydia Wang if (err < 0) 555225eaba2fSLydia Wang return err; 555325eaba2fSLydia Wang 555425eaba2fSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 555525eaba2fSLydia Wang return 0; /* can't find valid BIOS pin config */ 555625eaba2fSLydia Wang 555725eaba2fSLydia Wang err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg); 555825eaba2fSLydia Wang if (err < 0) 555925eaba2fSLydia Wang return err; 556025eaba2fSLydia Wang err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 556125eaba2fSLydia Wang if (err < 0) 556225eaba2fSLydia Wang return err; 556310a20af7STakashi Iwai err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg); 556425eaba2fSLydia Wang if (err < 0) 556525eaba2fSLydia Wang return err; 556625eaba2fSLydia Wang 556725eaba2fSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 556825eaba2fSLydia Wang 556925eaba2fSLydia Wang fill_dig_outs(codec); 557025eaba2fSLydia Wang 557125eaba2fSLydia Wang if (spec->kctls.list) 557225eaba2fSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 557325eaba2fSLydia Wang 557425eaba2fSLydia Wang spec->input_mux = &spec->private_imux[0]; 557525eaba2fSLydia Wang 557625eaba2fSLydia Wang if (spec->hp_mux) 55773d83e577STakashi Iwai via_hp_build(codec); 557825eaba2fSLydia Wang 557925eaba2fSLydia Wang return 1; 558025eaba2fSLydia Wang } 558125eaba2fSLydia Wang 558225eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 558325eaba2fSLydia Wang static struct hda_amp_list vt2002P_loopbacks[] = { 558425eaba2fSLydia Wang { 0x21, HDA_INPUT, 0 }, 558525eaba2fSLydia Wang { 0x21, HDA_INPUT, 1 }, 558625eaba2fSLydia Wang { 0x21, HDA_INPUT, 2 }, 558725eaba2fSLydia Wang { } /* end */ 558825eaba2fSLydia Wang }; 558925eaba2fSLydia Wang #endif 559025eaba2fSLydia Wang 559125eaba2fSLydia Wang 559225eaba2fSLydia Wang /* patch for vt2002P */ 559325eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 559425eaba2fSLydia Wang { 559525eaba2fSLydia Wang struct via_spec *spec; 559625eaba2fSLydia Wang int err; 559725eaba2fSLydia Wang 559825eaba2fSLydia Wang /* create a codec specific record */ 55995b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 560025eaba2fSLydia Wang if (spec == NULL) 560125eaba2fSLydia Wang return -ENOMEM; 560225eaba2fSLydia Wang 560325eaba2fSLydia Wang /* automatic parse from the BIOS config */ 560425eaba2fSLydia Wang err = vt2002P_parse_auto_config(codec); 560525eaba2fSLydia Wang if (err < 0) { 560625eaba2fSLydia Wang via_free(codec); 560725eaba2fSLydia Wang return err; 560825eaba2fSLydia Wang } else if (!err) { 560925eaba2fSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 561025eaba2fSLydia Wang "from BIOS. Using genenic mode...\n"); 561125eaba2fSLydia Wang } 561225eaba2fSLydia Wang 561325eaba2fSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt2002P_volume_init_verbs; 561425eaba2fSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs; 561525eaba2fSLydia Wang 561625eaba2fSLydia Wang spec->stream_name_analog = "VT2002P Analog"; 561725eaba2fSLydia Wang spec->stream_analog_playback = &vt2002P_pcm_analog_playback; 561825eaba2fSLydia Wang spec->stream_analog_capture = &vt2002P_pcm_analog_capture; 561925eaba2fSLydia Wang 562025eaba2fSLydia Wang spec->stream_name_digital = "VT2002P Digital"; 562125eaba2fSLydia Wang spec->stream_digital_playback = &vt2002P_pcm_digital_playback; 562225eaba2fSLydia Wang 562325eaba2fSLydia Wang if (!spec->adc_nids && spec->input_mux) { 562425eaba2fSLydia Wang spec->adc_nids = vt2002P_adc_nids; 562525eaba2fSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids); 562625eaba2fSLydia Wang get_mux_nids(codec); 562725eaba2fSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 562825eaba2fSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 562925eaba2fSLydia Wang spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; 563025eaba2fSLydia Wang spec->num_mixers++; 563125eaba2fSLydia Wang } 563225eaba2fSLydia Wang 563325eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 563425eaba2fSLydia Wang 563525eaba2fSLydia Wang codec->patch_ops.init = via_auto_init; 56360f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 563725eaba2fSLydia Wang 563825eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 563925eaba2fSLydia Wang spec->loopback.amplist = vt2002P_loopbacks; 564025eaba2fSLydia Wang #endif 564125eaba2fSLydia Wang 564225eaba2fSLydia Wang return 0; 564325eaba2fSLydia Wang } 5644ab6734e7SLydia Wang 5645ab6734e7SLydia Wang /* for vt1812 */ 5646ab6734e7SLydia Wang 5647ab6734e7SLydia Wang /* capture mixer elements */ 5648ab6734e7SLydia Wang static struct snd_kcontrol_new vt1812_capture_mixer[] = { 5649ab6734e7SLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 5650ab6734e7SLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 5651ab6734e7SLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 5652ab6734e7SLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 5653ab6734e7SLydia Wang HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 5654ab6734e7SLydia Wang HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, 5655ab6734e7SLydia Wang HDA_INPUT), 5656ab6734e7SLydia Wang { 5657ab6734e7SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5658ab6734e7SLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 5659ab6734e7SLydia Wang * So call somewhat different.. 5660ab6734e7SLydia Wang */ 5661ab6734e7SLydia Wang .name = "Input Source", 5662ab6734e7SLydia Wang .count = 2, 5663ab6734e7SLydia Wang .info = via_mux_enum_info, 5664ab6734e7SLydia Wang .get = via_mux_enum_get, 5665ab6734e7SLydia Wang .put = via_mux_enum_put, 5666ab6734e7SLydia Wang }, 5667ab6734e7SLydia Wang { } /* end */ 5668ab6734e7SLydia Wang }; 5669ab6734e7SLydia Wang 5670ab6734e7SLydia Wang static struct hda_verb vt1812_volume_init_verbs[] = { 5671ab6734e7SLydia Wang /* 5672ab6734e7SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 5673ab6734e7SLydia Wang */ 5674ab6734e7SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5675ab6734e7SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5676ab6734e7SLydia Wang 5677ab6734e7SLydia Wang 5678ab6734e7SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 5679ab6734e7SLydia Wang * mixer widget 5680ab6734e7SLydia Wang */ 5681ab6734e7SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 5682ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 5683ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 5684ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 5685ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 5686ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 5687ab6734e7SLydia Wang 5688ab6734e7SLydia Wang /* MUX Indices: Mic = 0 */ 5689ab6734e7SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 5690ab6734e7SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 5691ab6734e7SLydia Wang 5692ab6734e7SLydia Wang /* PW9 Output enable */ 5693ab6734e7SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 5694ab6734e7SLydia Wang 5695ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 5696ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 5697ab6734e7SLydia Wang 5698ab6734e7SLydia Wang /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 5699ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5700ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5701ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5702ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5703ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5704ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5705ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5706ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5707ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5708ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5709ab6734e7SLydia Wang 5710ab6734e7SLydia Wang /* set MUX0/1/4/13/15 = 0 (AOW0) */ 5711ab6734e7SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 5712ab6734e7SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 5713ab6734e7SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 5714ab6734e7SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 5715ab6734e7SLydia Wang {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, 5716ab6734e7SLydia Wang 5717ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 5718ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 5719ab6734e7SLydia Wang { } 5720ab6734e7SLydia Wang }; 5721ab6734e7SLydia Wang 5722ab6734e7SLydia Wang 5723ab6734e7SLydia Wang static struct hda_verb vt1812_uniwill_init_verbs[] = { 5724ab6734e7SLydia Wang {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, 5725ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5726ab6734e7SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, 5727ab6734e7SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 5728ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5729ab6734e7SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5730ab6734e7SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5731ab6734e7SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5732ab6734e7SLydia Wang { } 5733ab6734e7SLydia Wang }; 5734ab6734e7SLydia Wang 5735ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_playback = { 5736ab6734e7SLydia Wang .substreams = 2, 5737ab6734e7SLydia Wang .channels_min = 2, 5738ab6734e7SLydia Wang .channels_max = 2, 5739ab6734e7SLydia Wang .nid = 0x8, /* NID to query formats and rates */ 5740ab6734e7SLydia Wang .ops = { 5741ab6734e7SLydia Wang .open = via_playback_pcm_open, 5742ab6734e7SLydia Wang .prepare = via_playback_multi_pcm_prepare, 5743ab6734e7SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 5744ab6734e7SLydia Wang .close = via_pcm_open_close, 5745ab6734e7SLydia Wang }, 5746ab6734e7SLydia Wang }; 5747ab6734e7SLydia Wang 5748ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_capture = { 5749ab6734e7SLydia Wang .substreams = 2, 5750ab6734e7SLydia Wang .channels_min = 2, 5751ab6734e7SLydia Wang .channels_max = 2, 5752ab6734e7SLydia Wang .nid = 0x10, /* NID to query formats and rates */ 5753ab6734e7SLydia Wang .ops = { 5754ab6734e7SLydia Wang .open = via_pcm_open_close, 5755ab6734e7SLydia Wang .prepare = via_capture_pcm_prepare, 5756ab6734e7SLydia Wang .cleanup = via_capture_pcm_cleanup, 5757ab6734e7SLydia Wang .close = via_pcm_open_close, 5758ab6734e7SLydia Wang }, 5759ab6734e7SLydia Wang }; 5760ab6734e7SLydia Wang 5761ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_digital_playback = { 5762ab6734e7SLydia Wang .substreams = 1, 5763ab6734e7SLydia Wang .channels_min = 2, 5764ab6734e7SLydia Wang .channels_max = 2, 5765ab6734e7SLydia Wang /* NID is set in via_build_pcms */ 5766ab6734e7SLydia Wang .ops = { 5767ab6734e7SLydia Wang .open = via_dig_playback_pcm_open, 5768ab6734e7SLydia Wang .close = via_dig_playback_pcm_close, 5769ab6734e7SLydia Wang .prepare = via_dig_playback_pcm_prepare, 5770ab6734e7SLydia Wang .cleanup = via_dig_playback_pcm_cleanup 5771ab6734e7SLydia Wang }, 5772ab6734e7SLydia Wang }; 5773ab6734e7SLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 5774ab6734e7SLydia Wang static int vt1812_auto_fill_dac_nids(struct via_spec *spec, 5775ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5776ab6734e7SLydia Wang { 5777ab6734e7SLydia Wang spec->multiout.num_dacs = 1; 5778ab6734e7SLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 5779ab6734e7SLydia Wang if (cfg->line_out_pins[0]) 5780ab6734e7SLydia Wang spec->multiout.dac_nids[0] = 0x8; 5781ab6734e7SLydia Wang return 0; 5782ab6734e7SLydia Wang } 5783ab6734e7SLydia Wang 5784ab6734e7SLydia Wang 5785ab6734e7SLydia Wang /* add playback controls from the parsed DAC table */ 5786ab6734e7SLydia Wang static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec, 5787ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5788ab6734e7SLydia Wang { 5789ab6734e7SLydia Wang int err; 5790ab6734e7SLydia Wang 5791ab6734e7SLydia Wang if (!cfg->line_out_pins[0]) 5792ab6734e7SLydia Wang return -1; 5793ab6734e7SLydia Wang 5794ab6734e7SLydia Wang /* Line-Out: PortE */ 5795ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 57963d83e577STakashi Iwai "Front Playback Volume", 5797ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); 5798ab6734e7SLydia Wang if (err < 0) 5799ab6734e7SLydia Wang return err; 5800ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, 58013d83e577STakashi Iwai "Front Playback Switch", 5802ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT)); 5803ab6734e7SLydia Wang if (err < 0) 5804ab6734e7SLydia Wang return err; 5805ab6734e7SLydia Wang 5806ab6734e7SLydia Wang return 0; 5807ab6734e7SLydia Wang } 5808ab6734e7SLydia Wang 5809ab6734e7SLydia Wang static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 5810ab6734e7SLydia Wang { 5811ab6734e7SLydia Wang int err; 5812ab6734e7SLydia Wang 5813ab6734e7SLydia Wang if (!pin) 5814ab6734e7SLydia Wang return 0; 5815ab6734e7SLydia Wang 5816ab6734e7SLydia Wang spec->multiout.hp_nid = 0x9; 5817ab6734e7SLydia Wang spec->hp_independent_mode_index = 1; 5818ab6734e7SLydia Wang 5819ab6734e7SLydia Wang 5820ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 5821ab6734e7SLydia Wang "Headphone Playback Volume", 5822ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL( 5823ab6734e7SLydia Wang spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); 5824ab6734e7SLydia Wang if (err < 0) 5825ab6734e7SLydia Wang return err; 5826ab6734e7SLydia Wang 5827ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 5828ab6734e7SLydia Wang "Headphone Playback Switch", 5829ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 5830ab6734e7SLydia Wang if (err < 0) 5831ab6734e7SLydia Wang return err; 5832ab6734e7SLydia Wang 5833ab6734e7SLydia Wang create_hp_imux(spec); 5834ab6734e7SLydia Wang return 0; 5835ab6734e7SLydia Wang } 5836ab6734e7SLydia Wang 5837ab6734e7SLydia Wang /* create playback/capture controls for input pins */ 583810a20af7STakashi Iwai static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec, 5839ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5840ab6734e7SLydia Wang { 584110a20af7STakashi Iwai struct via_spec *spec = codec->spec; 5842ab6734e7SLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 5843f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff }; 5844f3268512STakashi Iwai int err; 5845ab6734e7SLydia Wang 584610a20af7STakashi Iwai err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, 5847f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 5848ab6734e7SLydia Wang if (err < 0) 5849ab6734e7SLydia Wang return err; 5850f3268512STakashi Iwai 5851ab6734e7SLydia Wang /* build volume/mute control of loopback */ 58527b315bb4STakashi Iwai err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21); 5853ab6734e7SLydia Wang if (err < 0) 5854ab6734e7SLydia Wang return err; 5855ab6734e7SLydia Wang 5856ab6734e7SLydia Wang /* for digital mic select */ 585710a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL); 5858ab6734e7SLydia Wang 5859ab6734e7SLydia Wang return 0; 5860ab6734e7SLydia Wang } 5861ab6734e7SLydia Wang 5862ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec) 5863ab6734e7SLydia Wang { 5864ab6734e7SLydia Wang struct via_spec *spec = codec->spec; 5865ab6734e7SLydia Wang int err; 5866ab6734e7SLydia Wang 5867ab6734e7SLydia Wang 5868ab6734e7SLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 5869ab6734e7SLydia Wang if (err < 0) 5870ab6734e7SLydia Wang return err; 5871ab6734e7SLydia Wang fill_dig_outs(codec); 5872ab6734e7SLydia Wang err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg); 5873ab6734e7SLydia Wang if (err < 0) 5874ab6734e7SLydia Wang return err; 5875ab6734e7SLydia Wang 5876ab6734e7SLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) 5877ab6734e7SLydia Wang return 0; /* can't find valid BIOS pin config */ 5878ab6734e7SLydia Wang 5879ab6734e7SLydia Wang err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg); 5880ab6734e7SLydia Wang if (err < 0) 5881ab6734e7SLydia Wang return err; 5882ab6734e7SLydia Wang err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 5883ab6734e7SLydia Wang if (err < 0) 5884ab6734e7SLydia Wang return err; 588510a20af7STakashi Iwai err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg); 5886ab6734e7SLydia Wang if (err < 0) 5887ab6734e7SLydia Wang return err; 5888ab6734e7SLydia Wang 5889ab6734e7SLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 5890ab6734e7SLydia Wang 5891ab6734e7SLydia Wang fill_dig_outs(codec); 5892ab6734e7SLydia Wang 5893ab6734e7SLydia Wang if (spec->kctls.list) 5894ab6734e7SLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 5895ab6734e7SLydia Wang 5896ab6734e7SLydia Wang spec->input_mux = &spec->private_imux[0]; 5897ab6734e7SLydia Wang 5898ab6734e7SLydia Wang if (spec->hp_mux) 58993d83e577STakashi Iwai via_hp_build(codec); 5900ab6734e7SLydia Wang 5901ab6734e7SLydia Wang return 1; 5902ab6734e7SLydia Wang } 5903ab6734e7SLydia Wang 5904ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5905ab6734e7SLydia Wang static struct hda_amp_list vt1812_loopbacks[] = { 5906ab6734e7SLydia Wang { 0x21, HDA_INPUT, 0 }, 5907ab6734e7SLydia Wang { 0x21, HDA_INPUT, 1 }, 5908ab6734e7SLydia Wang { 0x21, HDA_INPUT, 2 }, 5909ab6734e7SLydia Wang { } /* end */ 5910ab6734e7SLydia Wang }; 5911ab6734e7SLydia Wang #endif 5912ab6734e7SLydia Wang 5913ab6734e7SLydia Wang 5914ab6734e7SLydia Wang /* patch for vt1812 */ 5915ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 5916ab6734e7SLydia Wang { 5917ab6734e7SLydia Wang struct via_spec *spec; 5918ab6734e7SLydia Wang int err; 5919ab6734e7SLydia Wang 5920ab6734e7SLydia Wang /* create a codec specific record */ 59215b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 5922ab6734e7SLydia Wang if (spec == NULL) 5923ab6734e7SLydia Wang return -ENOMEM; 5924ab6734e7SLydia Wang 5925ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 5926ab6734e7SLydia Wang err = vt1812_parse_auto_config(codec); 5927ab6734e7SLydia Wang if (err < 0) { 5928ab6734e7SLydia Wang via_free(codec); 5929ab6734e7SLydia Wang return err; 5930ab6734e7SLydia Wang } else if (!err) { 5931ab6734e7SLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 5932ab6734e7SLydia Wang "from BIOS. Using genenic mode...\n"); 5933ab6734e7SLydia Wang } 5934ab6734e7SLydia Wang 5935ab6734e7SLydia Wang 5936ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; 5937ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; 5938ab6734e7SLydia Wang 5939ab6734e7SLydia Wang spec->stream_name_analog = "VT1812 Analog"; 5940ab6734e7SLydia Wang spec->stream_analog_playback = &vt1812_pcm_analog_playback; 5941ab6734e7SLydia Wang spec->stream_analog_capture = &vt1812_pcm_analog_capture; 5942ab6734e7SLydia Wang 5943ab6734e7SLydia Wang spec->stream_name_digital = "VT1812 Digital"; 5944ab6734e7SLydia Wang spec->stream_digital_playback = &vt1812_pcm_digital_playback; 5945ab6734e7SLydia Wang 5946ab6734e7SLydia Wang 5947ab6734e7SLydia Wang if (!spec->adc_nids && spec->input_mux) { 5948ab6734e7SLydia Wang spec->adc_nids = vt1812_adc_nids; 5949ab6734e7SLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids); 5950ab6734e7SLydia Wang get_mux_nids(codec); 5951ab6734e7SLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 5952ab6734e7SLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 5953ab6734e7SLydia Wang spec->mixers[spec->num_mixers] = vt1812_capture_mixer; 5954ab6734e7SLydia Wang spec->num_mixers++; 5955ab6734e7SLydia Wang } 5956ab6734e7SLydia Wang 5957ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 5958ab6734e7SLydia Wang 5959ab6734e7SLydia Wang codec->patch_ops.init = via_auto_init; 59600f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 5961ab6734e7SLydia Wang 5962ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5963ab6734e7SLydia Wang spec->loopback.amplist = vt1812_loopbacks; 5964ab6734e7SLydia Wang #endif 5965ab6734e7SLydia Wang 5966ab6734e7SLydia Wang return 0; 5967ab6734e7SLydia Wang } 5968ab6734e7SLydia Wang 5969c577b8a1SJoseph Chan /* 5970c577b8a1SJoseph Chan * patch entries 5971c577b8a1SJoseph Chan */ 59721289e9e8STakashi Iwai static struct hda_codec_preset snd_hda_preset_via[] = { 59733218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 59743218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 59753218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 59763218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 59773218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 5978f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 59793218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 5980f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 59813218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 5982f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 59833218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 5984f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 59853218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 5986f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 59873218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 5988f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 59893218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 5990f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 59913218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 5992f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 59933218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 5994f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 59953218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 5996f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 59973218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 5998f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 59993218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 6000f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 60013218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 6002f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 60033218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 6004f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 60053218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 6006f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 60073218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 6008f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 60093218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 6010d949cac1SHarald Welte .patch = patch_vt1708S}, 60113218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 6012d949cac1SHarald Welte .patch = patch_vt1708S}, 60133218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 6014d949cac1SHarald Welte .patch = patch_vt1708S}, 60153218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 6016d949cac1SHarald Welte .patch = patch_vt1708S}, 60173218c178STakashi Iwai { .id = 0x11064397, .name = "VT1708S", 6018d949cac1SHarald Welte .patch = patch_vt1708S}, 60193218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 6020d949cac1SHarald Welte .patch = patch_vt1708S}, 60213218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 6022d949cac1SHarald Welte .patch = patch_vt1708S}, 60233218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 6024d949cac1SHarald Welte .patch = patch_vt1708S}, 60253218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 6026d949cac1SHarald Welte .patch = patch_vt1702}, 60273218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 6028d949cac1SHarald Welte .patch = patch_vt1702}, 60293218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 6030d949cac1SHarald Welte .patch = patch_vt1702}, 60313218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 6032d949cac1SHarald Welte .patch = patch_vt1702}, 60333218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 6034d949cac1SHarald Welte .patch = patch_vt1702}, 60353218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 6036d949cac1SHarald Welte .patch = patch_vt1702}, 60373218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 6038d949cac1SHarald Welte .patch = patch_vt1702}, 60393218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 6040d949cac1SHarald Welte .patch = patch_vt1702}, 6041eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 6042eb7188caSLydia Wang .patch = patch_vt1718S}, 6043eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 6044eb7188caSLydia Wang .patch = patch_vt1718S}, 6045bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 6046bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 6047bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 6048bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 6049f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 6050f3db423dSLydia Wang .patch = patch_vt1716S}, 6051f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 6052f3db423dSLydia Wang .patch = patch_vt1716S}, 605325eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 605425eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 6055ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 605636dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 605736dd5c4aSLydia Wang .patch = patch_vt1708S}, 6058c577b8a1SJoseph Chan {} /* terminator */ 6059c577b8a1SJoseph Chan }; 60601289e9e8STakashi Iwai 60611289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 60621289e9e8STakashi Iwai 60631289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 60641289e9e8STakashi Iwai .preset = snd_hda_preset_via, 60651289e9e8STakashi Iwai .owner = THIS_MODULE, 60661289e9e8STakashi Iwai }; 60671289e9e8STakashi Iwai 60681289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 60691289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 60701289e9e8STakashi Iwai 60711289e9e8STakashi Iwai static int __init patch_via_init(void) 60721289e9e8STakashi Iwai { 60731289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 60741289e9e8STakashi Iwai } 60751289e9e8STakashi Iwai 60761289e9e8STakashi Iwai static void __exit patch_via_exit(void) 60771289e9e8STakashi Iwai { 60781289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 60791289e9e8STakashi Iwai } 60801289e9e8STakashi Iwai 60811289e9e8STakashi Iwai module_init(patch_via_init) 60821289e9e8STakashi Iwai module_exit(patch_via_exit) 6083