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 */ 44c577b8a1SJoseph Chan /* */ 45c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 46c577b8a1SJoseph Chan 47c577b8a1SJoseph Chan 48c577b8a1SJoseph Chan #include <linux/init.h> 49c577b8a1SJoseph Chan #include <linux/delay.h> 50c577b8a1SJoseph Chan #include <linux/slab.h> 51c577b8a1SJoseph Chan #include <sound/core.h> 520aa62aefSHarald Welte #include <sound/asoundef.h> 53c577b8a1SJoseph Chan #include "hda_codec.h" 54c577b8a1SJoseph Chan #include "hda_local.h" 55c577b8a1SJoseph Chan 56c577b8a1SJoseph Chan /* amp values */ 57c577b8a1SJoseph Chan #define AMP_VAL_IDX_SHIFT 19 58c577b8a1SJoseph Chan #define AMP_VAL_IDX_MASK (0x0f<<19) 59c577b8a1SJoseph Chan 60c577b8a1SJoseph Chan /* Pin Widget NID */ 61c577b8a1SJoseph Chan #define VT1708_HP_NID 0x13 62c577b8a1SJoseph Chan #define VT1708_DIGOUT_NID 0x14 63c577b8a1SJoseph Chan #define VT1708_DIGIN_NID 0x16 64f7278fd0SJosepch Chan #define VT1708_DIGIN_PIN 0x26 6576d9b0ddSHarald Welte #define VT1708_HP_PIN_NID 0x20 6676d9b0ddSHarald Welte #define VT1708_CD_PIN_NID 0x24 67c577b8a1SJoseph Chan 68c577b8a1SJoseph Chan #define VT1709_HP_DAC_NID 0x28 69c577b8a1SJoseph Chan #define VT1709_DIGOUT_NID 0x13 70c577b8a1SJoseph Chan #define VT1709_DIGIN_NID 0x17 71f7278fd0SJosepch Chan #define VT1709_DIGIN_PIN 0x25 72f7278fd0SJosepch Chan 73f7278fd0SJosepch Chan #define VT1708B_HP_NID 0x25 74f7278fd0SJosepch Chan #define VT1708B_DIGOUT_NID 0x12 75f7278fd0SJosepch Chan #define VT1708B_DIGIN_NID 0x15 76f7278fd0SJosepch Chan #define VT1708B_DIGIN_PIN 0x21 77c577b8a1SJoseph Chan 78d949cac1SHarald Welte #define VT1708S_HP_NID 0x25 79d949cac1SHarald Welte #define VT1708S_DIGOUT_NID 0x12 80d949cac1SHarald Welte 81d949cac1SHarald Welte #define VT1702_HP_NID 0x17 82d949cac1SHarald Welte #define VT1702_DIGOUT_NID 0x11 83d949cac1SHarald Welte 84d7426329SHarald Welte enum VIA_HDA_CODEC { 85d7426329SHarald Welte UNKNOWN = -1, 86d7426329SHarald Welte VT1708, 87d7426329SHarald Welte VT1709_10CH, 88d7426329SHarald Welte VT1709_6CH, 89d7426329SHarald Welte VT1708B_8CH, 90d7426329SHarald Welte VT1708B_4CH, 91d7426329SHarald Welte VT1708S, 92518bf3baSLydia Wang VT1708BCE, 93d7426329SHarald Welte VT1702, 94eb7188caSLydia Wang VT1718S, 95f3db423dSLydia Wang VT1716S, 9625eaba2fSLydia Wang VT2002P, 97ab6734e7SLydia Wang VT1812, 98d7426329SHarald Welte CODEC_TYPES, 99d7426329SHarald Welte }; 100d7426329SHarald Welte 1011f2e99feSLydia Wang struct via_spec { 1021f2e99feSLydia Wang /* codec parameterization */ 103f3db423dSLydia Wang struct snd_kcontrol_new *mixers[6]; 1041f2e99feSLydia Wang unsigned int num_mixers; 1051f2e99feSLydia Wang 1061f2e99feSLydia Wang struct hda_verb *init_verbs[5]; 1071f2e99feSLydia Wang unsigned int num_iverbs; 1081f2e99feSLydia Wang 1091f2e99feSLydia Wang char *stream_name_analog; 1101f2e99feSLydia Wang struct hda_pcm_stream *stream_analog_playback; 1111f2e99feSLydia Wang struct hda_pcm_stream *stream_analog_capture; 1121f2e99feSLydia Wang 1131f2e99feSLydia Wang char *stream_name_digital; 1141f2e99feSLydia Wang struct hda_pcm_stream *stream_digital_playback; 1151f2e99feSLydia Wang struct hda_pcm_stream *stream_digital_capture; 1161f2e99feSLydia Wang 1171f2e99feSLydia Wang /* playback */ 1181f2e99feSLydia Wang struct hda_multi_out multiout; 1191f2e99feSLydia Wang hda_nid_t slave_dig_outs[2]; 1201f2e99feSLydia Wang 1211f2e99feSLydia Wang /* capture */ 1221f2e99feSLydia Wang unsigned int num_adc_nids; 1231f2e99feSLydia Wang hda_nid_t *adc_nids; 1241f2e99feSLydia Wang hda_nid_t mux_nids[3]; 1251f2e99feSLydia Wang hda_nid_t dig_in_nid; 1261f2e99feSLydia Wang hda_nid_t dig_in_pin; 1271f2e99feSLydia Wang 1281f2e99feSLydia Wang /* capture source */ 1291f2e99feSLydia Wang const struct hda_input_mux *input_mux; 1301f2e99feSLydia Wang unsigned int cur_mux[3]; 1311f2e99feSLydia Wang 1321f2e99feSLydia Wang /* PCM information */ 1331f2e99feSLydia Wang struct hda_pcm pcm_rec[3]; 1341f2e99feSLydia Wang 1351f2e99feSLydia Wang /* dynamic controls, init_verbs and input_mux */ 1361f2e99feSLydia Wang struct auto_pin_cfg autocfg; 1371f2e99feSLydia Wang struct snd_array kctls; 1381f2e99feSLydia Wang struct hda_input_mux private_imux[2]; 1391f2e99feSLydia Wang hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 1401f2e99feSLydia Wang 1411f2e99feSLydia Wang /* HP mode source */ 1421f2e99feSLydia Wang const struct hda_input_mux *hp_mux; 1431f2e99feSLydia Wang unsigned int hp_independent_mode; 1441f2e99feSLydia Wang unsigned int hp_independent_mode_index; 1451f2e99feSLydia Wang unsigned int smart51_enabled; 146f3db423dSLydia Wang unsigned int dmic_enabled; 1471f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1481f2e99feSLydia Wang 1491f2e99feSLydia Wang /* work to check hp jack state */ 1501f2e99feSLydia Wang struct hda_codec *codec; 1511f2e99feSLydia Wang struct delayed_work vt1708_hp_work; 1521f2e99feSLydia Wang int vt1708_jack_detectect; 1531f2e99feSLydia Wang int vt1708_hp_present; 1541f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 1551f2e99feSLydia Wang struct hda_loopback_check loopback; 1561f2e99feSLydia Wang #endif 1571f2e99feSLydia Wang }; 1581f2e99feSLydia Wang 159744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 160d7426329SHarald Welte { 161744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 162d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 163d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 164d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 165d7426329SHarald Welte 166d7426329SHarald Welte /* get codec type */ 167d7426329SHarald Welte if (ven_id != 0x1106) 168d7426329SHarald Welte codec_type = UNKNOWN; 169d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 170d7426329SHarald Welte codec_type = VT1708; 171d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 172d7426329SHarald Welte codec_type = VT1709_10CH; 173d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 174d7426329SHarald Welte codec_type = VT1709_6CH; 175518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 176d7426329SHarald Welte codec_type = VT1708B_8CH; 177518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 178518bf3baSLydia Wang codec_type = VT1708BCE; 179518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 180d7426329SHarald Welte codec_type = VT1708B_4CH; 181d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 182d7426329SHarald Welte && (dev_id >> 12) < 8) 183d7426329SHarald Welte codec_type = VT1708S; 184d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 185d7426329SHarald Welte && (dev_id >> 12) < 8) 186d7426329SHarald Welte codec_type = VT1702; 187eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 188eb7188caSLydia Wang && (dev_id >> 12) < 8) 189eb7188caSLydia Wang codec_type = VT1718S; 190f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 191f3db423dSLydia Wang codec_type = VT1716S; 192bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 193bb3c6bfcSLydia Wang codec_type = VT1718S; 19425eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 19525eaba2fSLydia Wang codec_type = VT2002P; 196ab6734e7SLydia Wang else if (dev_id == 0x0448) 197ab6734e7SLydia Wang codec_type = VT1812; 198d7426329SHarald Welte else 199d7426329SHarald Welte codec_type = UNKNOWN; 200d7426329SHarald Welte return codec_type; 201d7426329SHarald Welte }; 202d7426329SHarald Welte 20369e52a80SHarald Welte #define VIA_HP_EVENT 0x01 20469e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 205a34df19aSLydia Wang #define VIA_JACK_EVENT 0x04 206f3db423dSLydia Wang #define VIA_MONO_EVENT 0x08 20725eaba2fSLydia Wang #define VIA_SPEAKER_EVENT 0x10 20825eaba2fSLydia Wang #define VIA_BIND_HP_EVENT 0x20 20969e52a80SHarald Welte 210c577b8a1SJoseph Chan enum { 211c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 212c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 213f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 21425eaba2fSLydia Wang VIA_CTL_WIDGET_BIND_PIN_MUTE, 215c577b8a1SJoseph Chan }; 216c577b8a1SJoseph Chan 217c577b8a1SJoseph Chan enum { 218eb14a46cSHarald Welte AUTO_SEQ_FRONT = 0, 219c577b8a1SJoseph Chan AUTO_SEQ_SURROUND, 220c577b8a1SJoseph Chan AUTO_SEQ_CENLFE, 221c577b8a1SJoseph Chan AUTO_SEQ_SIDE 222c577b8a1SJoseph Chan }; 223c577b8a1SJoseph Chan 224f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); 225f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec); 2261f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec); 2271f2e99feSLydia Wang 2281f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec) 2291f2e99feSLydia Wang { 2301f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2311f2e99feSLydia Wang return; 2321f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 2331f2e99feSLydia Wang !spec->vt1708_jack_detectect); 2341f2e99feSLydia Wang if (!delayed_work_pending(&spec->vt1708_hp_work)) 2351f2e99feSLydia Wang schedule_delayed_work(&spec->vt1708_hp_work, 2361f2e99feSLydia Wang msecs_to_jiffies(100)); 2371f2e99feSLydia Wang } 2381f2e99feSLydia Wang 2391f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec) 2401f2e99feSLydia Wang { 2411f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2421f2e99feSLydia Wang return; 2431f2e99feSLydia Wang if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 2441f2e99feSLydia Wang && !is_aa_path_mute(spec->codec)) 2451f2e99feSLydia Wang return; 2461f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 2471f2e99feSLydia Wang !spec->vt1708_jack_detectect); 2481f2e99feSLydia Wang cancel_delayed_work(&spec->vt1708_hp_work); 2491f2e99feSLydia Wang flush_scheduled_work(); 2501f2e99feSLydia Wang } 251f5271101SLydia Wang 25225eaba2fSLydia Wang 253f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 254f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 255f5271101SLydia Wang { 256f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 257f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 258f5271101SLydia Wang 259f5271101SLydia Wang set_jack_power_state(codec); 260f5271101SLydia Wang analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); 2611f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 2621f2e99feSLydia Wang if (is_aa_path_mute(codec)) 2631f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 2641f2e99feSLydia Wang else 2651f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 2661f2e99feSLydia Wang } 267f5271101SLydia Wang return change; 268f5271101SLydia Wang } 269f5271101SLydia Wang 270f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 271f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 272f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 273f5271101SLydia Wang .name = NULL, \ 274f5271101SLydia Wang .index = 0, \ 275f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 276f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 277f5271101SLydia Wang .put = analog_input_switch_put, \ 278f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 279f5271101SLydia Wang 28025eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec); 28125eaba2fSLydia Wang 28225eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, 28325eaba2fSLydia Wang struct snd_ctl_elem_value *ucontrol) 28425eaba2fSLydia Wang { 28525eaba2fSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 28625eaba2fSLydia Wang struct via_spec *spec = codec->spec; 28725eaba2fSLydia Wang int i; 28825eaba2fSLydia Wang int change = 0; 28925eaba2fSLydia Wang 29025eaba2fSLydia Wang long *valp = ucontrol->value.integer.value; 29125eaba2fSLydia Wang int lmute, rmute; 29225eaba2fSLydia Wang if (strstr(kcontrol->id.name, "Switch") == NULL) { 29325eaba2fSLydia Wang snd_printd("Invalid control!\n"); 29425eaba2fSLydia Wang return change; 29525eaba2fSLydia Wang } 29625eaba2fSLydia Wang change = snd_hda_mixer_amp_switch_put(kcontrol, 29725eaba2fSLydia Wang ucontrol); 29825eaba2fSLydia Wang /* Get mute value */ 29925eaba2fSLydia Wang lmute = *valp ? 0 : HDA_AMP_MUTE; 30025eaba2fSLydia Wang valp++; 30125eaba2fSLydia Wang rmute = *valp ? 0 : HDA_AMP_MUTE; 30225eaba2fSLydia Wang 30325eaba2fSLydia Wang /* Set hp pins */ 30425eaba2fSLydia Wang if (!spec->hp_independent_mode) { 30525eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 30625eaba2fSLydia Wang snd_hda_codec_amp_update( 30725eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 30825eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 30925eaba2fSLydia Wang lmute); 31025eaba2fSLydia Wang snd_hda_codec_amp_update( 31125eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 31225eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 31325eaba2fSLydia Wang rmute); 31425eaba2fSLydia Wang } 31525eaba2fSLydia Wang } 31625eaba2fSLydia Wang 31725eaba2fSLydia Wang if (!lmute && !rmute) { 31825eaba2fSLydia Wang /* Line Outs */ 31925eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 32025eaba2fSLydia Wang snd_hda_codec_amp_stereo( 32125eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 32225eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 32325eaba2fSLydia Wang /* Speakers */ 32425eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 32525eaba2fSLydia Wang snd_hda_codec_amp_stereo( 32625eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], 32725eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 32825eaba2fSLydia Wang /* unmute */ 32925eaba2fSLydia Wang via_hp_bind_automute(codec); 33025eaba2fSLydia Wang 33125eaba2fSLydia Wang } else { 33225eaba2fSLydia Wang if (lmute) { 33325eaba2fSLydia Wang /* Mute all left channels */ 33425eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 33525eaba2fSLydia Wang snd_hda_codec_amp_update( 33625eaba2fSLydia Wang codec, 33725eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 33825eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 33925eaba2fSLydia Wang lmute); 34025eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 34125eaba2fSLydia Wang snd_hda_codec_amp_update( 34225eaba2fSLydia Wang codec, 34325eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 34425eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 34525eaba2fSLydia Wang lmute); 34625eaba2fSLydia Wang } 34725eaba2fSLydia Wang if (rmute) { 34825eaba2fSLydia Wang /* mute all right channels */ 34925eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 35025eaba2fSLydia Wang snd_hda_codec_amp_update( 35125eaba2fSLydia Wang codec, 35225eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 35325eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 35425eaba2fSLydia Wang rmute); 35525eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 35625eaba2fSLydia Wang snd_hda_codec_amp_update( 35725eaba2fSLydia Wang codec, 35825eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 35925eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 36025eaba2fSLydia Wang rmute); 36125eaba2fSLydia Wang } 36225eaba2fSLydia Wang } 36325eaba2fSLydia Wang return change; 36425eaba2fSLydia Wang } 36525eaba2fSLydia Wang 36625eaba2fSLydia Wang #define BIND_PIN_MUTE \ 36725eaba2fSLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 36825eaba2fSLydia Wang .name = NULL, \ 36925eaba2fSLydia Wang .index = 0, \ 37025eaba2fSLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 37125eaba2fSLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 37225eaba2fSLydia Wang .put = bind_pin_switch_put, \ 37325eaba2fSLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 37425eaba2fSLydia Wang 37571eb7dccSLydia Wang static struct snd_kcontrol_new via_control_templates[] = { 376c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 377c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 378f5271101SLydia Wang ANALOG_INPUT_MUTE, 37925eaba2fSLydia Wang BIND_PIN_MUTE, 380c577b8a1SJoseph Chan }; 381c577b8a1SJoseph Chan 382c577b8a1SJoseph Chan static hda_nid_t vt1708_adc_nids[2] = { 383c577b8a1SJoseph Chan /* ADC1-2 */ 384c577b8a1SJoseph Chan 0x15, 0x27 385c577b8a1SJoseph Chan }; 386c577b8a1SJoseph Chan 387c577b8a1SJoseph Chan static hda_nid_t vt1709_adc_nids[3] = { 388c577b8a1SJoseph Chan /* ADC1-2 */ 389c577b8a1SJoseph Chan 0x14, 0x15, 0x16 390c577b8a1SJoseph Chan }; 391c577b8a1SJoseph Chan 392f7278fd0SJosepch Chan static hda_nid_t vt1708B_adc_nids[2] = { 393f7278fd0SJosepch Chan /* ADC1-2 */ 394f7278fd0SJosepch Chan 0x13, 0x14 395f7278fd0SJosepch Chan }; 396f7278fd0SJosepch Chan 397d949cac1SHarald Welte static hda_nid_t vt1708S_adc_nids[2] = { 398d949cac1SHarald Welte /* ADC1-2 */ 399d949cac1SHarald Welte 0x13, 0x14 400d949cac1SHarald Welte }; 401d949cac1SHarald Welte 402d949cac1SHarald Welte static hda_nid_t vt1702_adc_nids[3] = { 403d949cac1SHarald Welte /* ADC1-2 */ 404d949cac1SHarald Welte 0x12, 0x20, 0x1F 405d949cac1SHarald Welte }; 406d949cac1SHarald Welte 407eb7188caSLydia Wang static hda_nid_t vt1718S_adc_nids[2] = { 408eb7188caSLydia Wang /* ADC1-2 */ 409eb7188caSLydia Wang 0x10, 0x11 410eb7188caSLydia Wang }; 411eb7188caSLydia Wang 412f3db423dSLydia Wang static hda_nid_t vt1716S_adc_nids[2] = { 413f3db423dSLydia Wang /* ADC1-2 */ 414f3db423dSLydia Wang 0x13, 0x14 415f3db423dSLydia Wang }; 416f3db423dSLydia Wang 41725eaba2fSLydia Wang static hda_nid_t vt2002P_adc_nids[2] = { 41825eaba2fSLydia Wang /* ADC1-2 */ 41925eaba2fSLydia Wang 0x10, 0x11 42025eaba2fSLydia Wang }; 42125eaba2fSLydia Wang 422ab6734e7SLydia Wang static hda_nid_t vt1812_adc_nids[2] = { 423ab6734e7SLydia Wang /* ADC1-2 */ 424ab6734e7SLydia Wang 0x10, 0x11 425ab6734e7SLydia Wang }; 426ab6734e7SLydia Wang 427ab6734e7SLydia Wang 428c577b8a1SJoseph Chan /* add dynamic controls */ 429c577b8a1SJoseph Chan static int via_add_control(struct via_spec *spec, int type, const char *name, 430c577b8a1SJoseph Chan unsigned long val) 431c577b8a1SJoseph Chan { 432c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 433c577b8a1SJoseph Chan 434603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 435603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 436c577b8a1SJoseph Chan if (!knew) 437c577b8a1SJoseph Chan return -ENOMEM; 43871eb7dccSLydia Wang *knew = via_control_templates[type]; 439c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 440c577b8a1SJoseph Chan if (!knew->name) 441c577b8a1SJoseph Chan return -ENOMEM; 442c577b8a1SJoseph Chan knew->private_value = val; 443c577b8a1SJoseph Chan return 0; 444c577b8a1SJoseph Chan } 445c577b8a1SJoseph Chan 446603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 447603c4019STakashi Iwai { 448603c4019STakashi Iwai struct via_spec *spec = codec->spec; 449603c4019STakashi Iwai 450603c4019STakashi Iwai if (spec->kctls.list) { 451603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 452603c4019STakashi Iwai int i; 453603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 454603c4019STakashi Iwai kfree(kctl[i].name); 455603c4019STakashi Iwai } 456603c4019STakashi Iwai snd_array_free(&spec->kctls); 457603c4019STakashi Iwai } 458603c4019STakashi Iwai 459c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 4609510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 4619510e8ddSLydia Wang int idx, int mix_nid) 462c577b8a1SJoseph Chan { 463c577b8a1SJoseph Chan char name[32]; 464c577b8a1SJoseph Chan int err; 465c577b8a1SJoseph Chan 466c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 467c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 468c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 469c577b8a1SJoseph Chan if (err < 0) 470c577b8a1SJoseph Chan return err; 471c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 472f5271101SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, 473c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 474c577b8a1SJoseph Chan if (err < 0) 475c577b8a1SJoseph Chan return err; 476c577b8a1SJoseph Chan return 0; 477c577b8a1SJoseph Chan } 478c577b8a1SJoseph Chan 479c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec, 480c577b8a1SJoseph Chan hda_nid_t nid, int pin_type, 481c577b8a1SJoseph Chan int dac_idx) 482c577b8a1SJoseph Chan { 483c577b8a1SJoseph Chan /* set as output */ 484c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 485c577b8a1SJoseph Chan pin_type); 486c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 487c577b8a1SJoseph Chan AMP_OUT_UNMUTE); 488d3a11e60STakashi Iwai if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) 489d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 490d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 491c577b8a1SJoseph Chan } 492c577b8a1SJoseph Chan 493c577b8a1SJoseph Chan 494c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 495c577b8a1SJoseph Chan { 496c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 497c577b8a1SJoseph Chan int i; 498c577b8a1SJoseph Chan 499c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 500c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.line_out_pins[i]; 501c577b8a1SJoseph Chan if (nid) 502c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); 503c577b8a1SJoseph Chan } 504c577b8a1SJoseph Chan } 505c577b8a1SJoseph Chan 506c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 507c577b8a1SJoseph Chan { 508c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 509c577b8a1SJoseph Chan hda_nid_t pin; 51025eaba2fSLydia Wang int i; 511c577b8a1SJoseph Chan 51225eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 51325eaba2fSLydia Wang pin = spec->autocfg.hp_pins[i]; 514c577b8a1SJoseph Chan if (pin) /* connect to front */ 515c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); 516c577b8a1SJoseph Chan } 51725eaba2fSLydia Wang } 518c577b8a1SJoseph Chan 519c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 520c577b8a1SJoseph Chan { 521c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 522c577b8a1SJoseph Chan int i; 523c577b8a1SJoseph Chan 524c577b8a1SJoseph Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 525c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.input_pins[i]; 526c577b8a1SJoseph Chan 527c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 528c577b8a1SJoseph Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 529c577b8a1SJoseph Chan (i <= AUTO_PIN_FRONT_MIC ? 530c577b8a1SJoseph Chan PIN_VREF50 : PIN_IN)); 531c577b8a1SJoseph Chan 532c577b8a1SJoseph Chan } 533c577b8a1SJoseph Chan } 534f5271101SLydia Wang 5351564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); 5361564b287SLydia Wang 537f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 538f5271101SLydia Wang unsigned int *affected_parm) 539f5271101SLydia Wang { 540f5271101SLydia Wang unsigned parm; 541f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 542f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 543f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 544f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 545f5271101SLydia Wang unsigned present = snd_hda_codec_read(codec, nid, 0, 546f5271101SLydia Wang AC_VERB_GET_PIN_SENSE, 0) >> 31; 5471564b287SLydia Wang struct via_spec *spec = codec->spec; 5481564b287SLydia Wang if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) 5491564b287SLydia Wang || ((no_presence || present) 5501564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 551f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 552f5271101SLydia Wang parm = AC_PWRST_D0; 553f5271101SLydia Wang } else 554f5271101SLydia Wang parm = AC_PWRST_D3; 555f5271101SLydia Wang 556f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 557f5271101SLydia Wang } 558f5271101SLydia Wang 559f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec) 560f5271101SLydia Wang { 561f5271101SLydia Wang struct via_spec *spec = codec->spec; 562f5271101SLydia Wang int imux_is_smixer; 563f5271101SLydia Wang unsigned int parm; 564f5271101SLydia Wang 565f5271101SLydia Wang if (spec->codec_type == VT1702) { 566f5271101SLydia Wang imux_is_smixer = snd_hda_codec_read( 567f5271101SLydia Wang codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 568f5271101SLydia Wang /* inputs */ 569f5271101SLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 570f5271101SLydia Wang parm = AC_PWRST_D3; 571f5271101SLydia Wang set_pin_power_state(codec, 0x14, &parm); 572f5271101SLydia Wang set_pin_power_state(codec, 0x15, &parm); 573f5271101SLydia Wang set_pin_power_state(codec, 0x18, &parm); 574f5271101SLydia Wang if (imux_is_smixer) 575f5271101SLydia Wang parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */ 576f5271101SLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 577f5271101SLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, 578f5271101SLydia Wang parm); 579f5271101SLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, 580f5271101SLydia Wang parm); 581f5271101SLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, 582f5271101SLydia Wang parm); 583f5271101SLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, 584f5271101SLydia Wang parm); 585f5271101SLydia Wang 586f5271101SLydia Wang /* outputs */ 587f5271101SLydia Wang /* PW 3/4 (16h/17h) */ 588f5271101SLydia Wang parm = AC_PWRST_D3; 589f5271101SLydia Wang set_pin_power_state(codec, 0x16, &parm); 590f5271101SLydia Wang set_pin_power_state(codec, 0x17, &parm); 591f5271101SLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 592f5271101SLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 593f5271101SLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 594f5271101SLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 595f5271101SLydia Wang parm); 596f5271101SLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, 597f5271101SLydia Wang parm); 598f5271101SLydia Wang } else if (spec->codec_type == VT1708B_8CH 599f5271101SLydia Wang || spec->codec_type == VT1708B_4CH 600f5271101SLydia Wang || spec->codec_type == VT1708S) { 601f5271101SLydia Wang /* SW0 (17h) = stereo mixer */ 602f5271101SLydia Wang int is_8ch = spec->codec_type != VT1708B_4CH; 603f5271101SLydia Wang imux_is_smixer = snd_hda_codec_read( 604f5271101SLydia Wang codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 605f5271101SLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0); 606f5271101SLydia Wang /* inputs */ 607f5271101SLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 608f5271101SLydia Wang parm = AC_PWRST_D3; 609f5271101SLydia Wang set_pin_power_state(codec, 0x1a, &parm); 610f5271101SLydia Wang set_pin_power_state(codec, 0x1b, &parm); 611f5271101SLydia Wang set_pin_power_state(codec, 0x1e, &parm); 612f5271101SLydia Wang if (imux_is_smixer) 613f5271101SLydia Wang parm = AC_PWRST_D0; 614f5271101SLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 615f5271101SLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, 616f5271101SLydia Wang parm); 617f5271101SLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, 618f5271101SLydia Wang parm); 619f5271101SLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, 620f5271101SLydia Wang parm); 621f5271101SLydia Wang 622f5271101SLydia Wang /* outputs */ 623f5271101SLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 624f5271101SLydia Wang parm = AC_PWRST_D3; 625f5271101SLydia Wang set_pin_power_state(codec, 0x19, &parm); 626f5271101SLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, 627f5271101SLydia Wang parm); 628f5271101SLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, 629f5271101SLydia Wang parm); 630f5271101SLydia Wang 631f5271101SLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 632f5271101SLydia Wang if (is_8ch) { 633f5271101SLydia Wang parm = AC_PWRST_D3; 634f5271101SLydia Wang set_pin_power_state(codec, 0x22, &parm); 635f5271101SLydia Wang snd_hda_codec_write(codec, 0x26, 0, 636f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 637f5271101SLydia Wang snd_hda_codec_write(codec, 0x24, 0, 638f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 639f5271101SLydia Wang } 640f5271101SLydia Wang 641f5271101SLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 642f5271101SLydia Wang parm = AC_PWRST_D3; 643f5271101SLydia Wang /* force to D0 for internal Speaker */ 644f5271101SLydia Wang set_pin_power_state(codec, 0x1c, &parm); 645f5271101SLydia Wang set_pin_power_state(codec, 0x1d, &parm); 646f5271101SLydia Wang if (is_8ch) 647f5271101SLydia Wang set_pin_power_state(codec, 0x23, &parm); 648f5271101SLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 649f5271101SLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 650f5271101SLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 651f5271101SLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 652f5271101SLydia Wang parm); 653f5271101SLydia Wang if (is_8ch) { 654f5271101SLydia Wang snd_hda_codec_write(codec, 0x25, 0, 655f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 656f5271101SLydia Wang snd_hda_codec_write(codec, 0x27, 0, 657f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 658f5271101SLydia Wang } 659eb7188caSLydia Wang } else if (spec->codec_type == VT1718S) { 660eb7188caSLydia Wang /* MUX6 (1eh) = stereo mixer */ 661eb7188caSLydia Wang imux_is_smixer = snd_hda_codec_read( 662eb7188caSLydia Wang codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 663eb7188caSLydia Wang /* inputs */ 664eb7188caSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 665eb7188caSLydia Wang parm = AC_PWRST_D3; 666eb7188caSLydia Wang set_pin_power_state(codec, 0x29, &parm); 667eb7188caSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 668eb7188caSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 669eb7188caSLydia Wang if (imux_is_smixer) 670eb7188caSLydia Wang parm = AC_PWRST_D0; 671eb7188caSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 672eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, 673eb7188caSLydia Wang parm); 674eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, 675eb7188caSLydia Wang parm); 676eb7188caSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 677eb7188caSLydia Wang parm); 678eb7188caSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, 679eb7188caSLydia Wang parm); 680eb7188caSLydia Wang 681eb7188caSLydia Wang /* outputs */ 682eb7188caSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 683eb7188caSLydia Wang parm = AC_PWRST_D3; 684eb7188caSLydia Wang set_pin_power_state(codec, 0x27, &parm); 685eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 686eb7188caSLydia Wang parm); 687eb7188caSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, 688eb7188caSLydia Wang parm); 689eb7188caSLydia Wang 690eb7188caSLydia Wang /* PW2 (26h), AOW2 (ah) */ 691eb7188caSLydia Wang parm = AC_PWRST_D3; 692eb7188caSLydia Wang set_pin_power_state(codec, 0x26, &parm); 693eb7188caSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, 694eb7188caSLydia Wang parm); 695eb7188caSLydia Wang 696eb7188caSLydia Wang /* PW0/1 (24h/25h) */ 697eb7188caSLydia Wang parm = AC_PWRST_D3; 698eb7188caSLydia Wang set_pin_power_state(codec, 0x24, &parm); 699eb7188caSLydia Wang set_pin_power_state(codec, 0x25, &parm); 700eb7188caSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 701eb7188caSLydia Wang set_pin_power_state(codec, 0x28, &parm); 702eb7188caSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, 703eb7188caSLydia Wang parm); 704eb7188caSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, 705eb7188caSLydia Wang parm); 706eb7188caSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 707eb7188caSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 708eb7188caSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 709eb7188caSLydia Wang if (spec->hp_independent_mode) { 710eb7188caSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 711eb7188caSLydia Wang parm = AC_PWRST_D3; 712eb7188caSLydia Wang set_pin_power_state(codec, 0x28, &parm); 713eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 714eb7188caSLydia Wang AC_VERB_SET_POWER_STATE, parm); 715eb7188caSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 716eb7188caSLydia Wang AC_VERB_SET_POWER_STATE, parm); 717eb7188caSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 718eb7188caSLydia Wang AC_VERB_SET_POWER_STATE, parm); 719eb7188caSLydia Wang } 720f3db423dSLydia Wang } else if (spec->codec_type == VT1716S) { 721f3db423dSLydia Wang unsigned int mono_out, present; 722f3db423dSLydia Wang /* SW0 (17h) = stereo mixer */ 723f3db423dSLydia Wang imux_is_smixer = snd_hda_codec_read( 724f3db423dSLydia Wang codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 725f3db423dSLydia Wang /* inputs */ 726f3db423dSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 727f3db423dSLydia Wang parm = AC_PWRST_D3; 728f3db423dSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 729f3db423dSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 730f3db423dSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 731f3db423dSLydia Wang if (imux_is_smixer) 732f3db423dSLydia Wang parm = AC_PWRST_D0; 733f3db423dSLydia Wang /* SW0 (17h), AIW0(13h) */ 734f3db423dSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, 735f3db423dSLydia Wang parm); 736f3db423dSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, 737f3db423dSLydia Wang parm); 738f3db423dSLydia Wang 739f3db423dSLydia Wang parm = AC_PWRST_D3; 740f3db423dSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 741f3db423dSLydia Wang /* PW11 (22h) */ 742f3db423dSLydia Wang if (spec->dmic_enabled) 743f3db423dSLydia Wang set_pin_power_state(codec, 0x22, &parm); 744f3db423dSLydia Wang else 745f3db423dSLydia Wang snd_hda_codec_write( 746f3db423dSLydia Wang codec, 0x22, 0, 747f3db423dSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 748f3db423dSLydia Wang 749f3db423dSLydia Wang /* SW2(26h), AIW1(14h) */ 750f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, 751f3db423dSLydia Wang parm); 752f3db423dSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, 753f3db423dSLydia Wang parm); 754f3db423dSLydia Wang 755f3db423dSLydia Wang /* outputs */ 756f3db423dSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 757f3db423dSLydia Wang parm = AC_PWRST_D3; 758f3db423dSLydia Wang set_pin_power_state(codec, 0x19, &parm); 759f3db423dSLydia Wang /* Smart 5.1 PW2(1bh) */ 760f3db423dSLydia Wang if (spec->smart51_enabled) 761f3db423dSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 762f3db423dSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, 763f3db423dSLydia Wang parm); 764f3db423dSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, 765f3db423dSLydia Wang parm); 766f3db423dSLydia Wang 767f3db423dSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 768f3db423dSLydia Wang parm = AC_PWRST_D3; 769f3db423dSLydia Wang set_pin_power_state(codec, 0x23, &parm); 770f3db423dSLydia Wang /* Smart 5.1 PW1(1ah) */ 771f3db423dSLydia Wang if (spec->smart51_enabled) 772f3db423dSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 773f3db423dSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, 774f3db423dSLydia Wang parm); 775f3db423dSLydia Wang 776f3db423dSLydia Wang /* Smart 5.1 PW5(1eh) */ 777f3db423dSLydia Wang if (spec->smart51_enabled) 778f3db423dSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 779f3db423dSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, 780f3db423dSLydia Wang parm); 781f3db423dSLydia Wang 782f3db423dSLydia Wang /* Mono out */ 783f3db423dSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 784f3db423dSLydia Wang present = snd_hda_codec_read( 785f3db423dSLydia Wang codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 786f3db423dSLydia Wang if (present) 787f3db423dSLydia Wang mono_out = 0; 788f3db423dSLydia Wang else { 789f3db423dSLydia Wang present = snd_hda_codec_read( 790f3db423dSLydia Wang codec, 0x1d, 0, AC_VERB_GET_PIN_SENSE, 0) 791f3db423dSLydia Wang & 0x80000000; 792f3db423dSLydia Wang if (!spec->hp_independent_mode && present) 793f3db423dSLydia Wang mono_out = 0; 794f3db423dSLydia Wang else 795f3db423dSLydia Wang mono_out = 1; 796f3db423dSLydia Wang } 797f3db423dSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 798f3db423dSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, 799f3db423dSLydia Wang parm); 800f3db423dSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, 801f3db423dSLydia Wang parm); 802f3db423dSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, 803f3db423dSLydia Wang parm); 804f3db423dSLydia Wang 805f3db423dSLydia Wang /* PW 3/4 (1ch/1dh) */ 806f3db423dSLydia Wang parm = AC_PWRST_D3; 807f3db423dSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 808f3db423dSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 809f3db423dSLydia Wang /* HP Independent Mode, power on AOW3 */ 810f3db423dSLydia Wang if (spec->hp_independent_mode) 811f3db423dSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 812f3db423dSLydia Wang AC_VERB_SET_POWER_STATE, parm); 813f3db423dSLydia Wang 814f3db423dSLydia Wang /* force to D0 for internal Speaker */ 815f3db423dSLydia Wang /* MW0 (16h), AOW0 (10h) */ 816f3db423dSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 817f3db423dSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 818f3db423dSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 819f3db423dSLydia Wang mono_out ? AC_PWRST_D0 : parm); 82025eaba2fSLydia Wang } else if (spec->codec_type == VT2002P) { 82125eaba2fSLydia Wang unsigned int present; 82225eaba2fSLydia Wang /* MUX9 (1eh) = stereo mixer */ 82325eaba2fSLydia Wang imux_is_smixer = snd_hda_codec_read( 82425eaba2fSLydia Wang codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 82525eaba2fSLydia Wang /* inputs */ 82625eaba2fSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 82725eaba2fSLydia Wang parm = AC_PWRST_D3; 82825eaba2fSLydia Wang set_pin_power_state(codec, 0x29, &parm); 82925eaba2fSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 83025eaba2fSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 83125eaba2fSLydia Wang if (imux_is_smixer) 83225eaba2fSLydia Wang parm = AC_PWRST_D0; 83325eaba2fSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 83425eaba2fSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, 83525eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 83625eaba2fSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, 83725eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 83825eaba2fSLydia Wang snd_hda_codec_write(codec, 0x10, 0, 83925eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 84025eaba2fSLydia Wang snd_hda_codec_write(codec, 0x11, 0, 84125eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 84225eaba2fSLydia Wang 84325eaba2fSLydia Wang /* outputs */ 84425eaba2fSLydia Wang /* AOW0 (8h)*/ 84525eaba2fSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 84625eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 84725eaba2fSLydia Wang 84825eaba2fSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 84925eaba2fSLydia Wang parm = AC_PWRST_D3; 85025eaba2fSLydia Wang set_pin_power_state(codec, 0x26, &parm); 85125eaba2fSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 85225eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 85325eaba2fSLydia Wang snd_hda_codec_write(codec, 0x37, 85425eaba2fSLydia Wang 0, AC_VERB_SET_POWER_STATE, parm); 85525eaba2fSLydia Wang 85625eaba2fSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 85725eaba2fSLydia Wang parm = AC_PWRST_D3; 85825eaba2fSLydia Wang set_pin_power_state(codec, 0x25, &parm); 85925eaba2fSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 86025eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 86125eaba2fSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 86225eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 86325eaba2fSLydia Wang if (spec->hp_independent_mode) { 86425eaba2fSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 86525eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 86625eaba2fSLydia Wang } 86725eaba2fSLydia Wang 86825eaba2fSLydia Wang /* Class-D */ 86925eaba2fSLydia Wang /* PW0 (24h), MW0(18h), MUX0(34h) */ 87025eaba2fSLydia Wang present = snd_hda_codec_read( 87125eaba2fSLydia Wang codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 87225eaba2fSLydia Wang parm = AC_PWRST_D3; 87325eaba2fSLydia Wang set_pin_power_state(codec, 0x24, &parm); 87425eaba2fSLydia Wang if (present) { 87525eaba2fSLydia Wang snd_hda_codec_write( 87625eaba2fSLydia Wang codec, 0x18, 0, 87725eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 87825eaba2fSLydia Wang snd_hda_codec_write( 87925eaba2fSLydia Wang codec, 0x34, 0, 88025eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 88125eaba2fSLydia Wang } else { 88225eaba2fSLydia Wang snd_hda_codec_write( 88325eaba2fSLydia Wang codec, 0x18, 0, 88425eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 88525eaba2fSLydia Wang snd_hda_codec_write( 88625eaba2fSLydia Wang codec, 0x34, 0, 88725eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 88825eaba2fSLydia Wang } 88925eaba2fSLydia Wang 89025eaba2fSLydia Wang /* Mono Out */ 89125eaba2fSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 89225eaba2fSLydia Wang present = snd_hda_codec_read( 89325eaba2fSLydia Wang codec, 0x26, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 89425eaba2fSLydia Wang parm = AC_PWRST_D3; 89525eaba2fSLydia Wang set_pin_power_state(codec, 0x31, &parm); 89625eaba2fSLydia Wang if (present) { 89725eaba2fSLydia Wang snd_hda_codec_write( 89825eaba2fSLydia Wang codec, 0x17, 0, 89925eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 90025eaba2fSLydia Wang snd_hda_codec_write( 90125eaba2fSLydia Wang codec, 0x3b, 0, 90225eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 90325eaba2fSLydia Wang } else { 90425eaba2fSLydia Wang snd_hda_codec_write( 90525eaba2fSLydia Wang codec, 0x17, 0, 90625eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 90725eaba2fSLydia Wang snd_hda_codec_write( 90825eaba2fSLydia Wang codec, 0x3b, 0, 90925eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 91025eaba2fSLydia Wang } 91125eaba2fSLydia Wang 91225eaba2fSLydia Wang /* MW9 (21h) */ 91325eaba2fSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 91425eaba2fSLydia Wang snd_hda_codec_write( 91525eaba2fSLydia Wang codec, 0x21, 0, 91625eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 91725eaba2fSLydia Wang else 91825eaba2fSLydia Wang snd_hda_codec_write( 91925eaba2fSLydia Wang codec, 0x21, 0, 92025eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 921ab6734e7SLydia Wang } else if (spec->codec_type == VT1812) { 922ab6734e7SLydia Wang unsigned int present; 923ab6734e7SLydia Wang /* MUX10 (1eh) = stereo mixer */ 924ab6734e7SLydia Wang imux_is_smixer = snd_hda_codec_read( 925ab6734e7SLydia Wang codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 926ab6734e7SLydia Wang /* inputs */ 927ab6734e7SLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 928ab6734e7SLydia Wang parm = AC_PWRST_D3; 929ab6734e7SLydia Wang set_pin_power_state(codec, 0x29, &parm); 930ab6734e7SLydia Wang set_pin_power_state(codec, 0x2a, &parm); 931ab6734e7SLydia Wang set_pin_power_state(codec, 0x2b, &parm); 932ab6734e7SLydia Wang if (imux_is_smixer) 933ab6734e7SLydia Wang parm = AC_PWRST_D0; 934ab6734e7SLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 935ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x1e, 0, 936ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 937ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x1f, 0, 938ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 939ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x10, 0, 940ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 941ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x11, 0, 942ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 943ab6734e7SLydia Wang 944ab6734e7SLydia Wang /* outputs */ 945ab6734e7SLydia Wang /* AOW0 (8h)*/ 946ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x8, 0, 947ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 948ab6734e7SLydia Wang 949ab6734e7SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 950ab6734e7SLydia Wang parm = AC_PWRST_D3; 951ab6734e7SLydia Wang set_pin_power_state(codec, 0x28, &parm); 952ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x18, 0, 953ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 954ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x38, 0, 955ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 956ab6734e7SLydia Wang 957ab6734e7SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 958ab6734e7SLydia Wang parm = AC_PWRST_D3; 959ab6734e7SLydia Wang set_pin_power_state(codec, 0x25, &parm); 960ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x15, 0, 961ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 962ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x35, 0, 963ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 964ab6734e7SLydia Wang if (spec->hp_independent_mode) { 965ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x9, 0, 966ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 967ab6734e7SLydia Wang } 968ab6734e7SLydia Wang 969ab6734e7SLydia Wang /* Internal Speaker */ 970ab6734e7SLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 971ab6734e7SLydia Wang present = snd_hda_codec_read( 972ab6734e7SLydia Wang codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 973ab6734e7SLydia Wang parm = AC_PWRST_D3; 974ab6734e7SLydia Wang set_pin_power_state(codec, 0x24, &parm); 975ab6734e7SLydia Wang if (present) { 976ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 977ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 978ab6734e7SLydia Wang AC_PWRST_D3); 979ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x34, 0, 980ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 981ab6734e7SLydia Wang AC_PWRST_D3); 982ab6734e7SLydia Wang } else { 983ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 984ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 985ab6734e7SLydia Wang AC_PWRST_D0); 986ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x34, 0, 987ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 988ab6734e7SLydia Wang AC_PWRST_D0); 989ab6734e7SLydia Wang } 990ab6734e7SLydia Wang /* Mono Out */ 991ab6734e7SLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 992ab6734e7SLydia Wang present = snd_hda_codec_read( 993ab6734e7SLydia Wang codec, 0x28, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 994ab6734e7SLydia Wang parm = AC_PWRST_D3; 995ab6734e7SLydia Wang set_pin_power_state(codec, 0x31, &parm); 996ab6734e7SLydia Wang if (present) { 997ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 998ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 999ab6734e7SLydia Wang AC_PWRST_D3); 1000ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 1001ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1002ab6734e7SLydia Wang AC_PWRST_D3); 1003ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 1004ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1005ab6734e7SLydia Wang AC_PWRST_D3); 1006ab6734e7SLydia Wang } else { 1007ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 1008ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1009ab6734e7SLydia Wang AC_PWRST_D0); 1010ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 1011ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1012ab6734e7SLydia Wang AC_PWRST_D0); 1013ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 1014ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1015ab6734e7SLydia Wang AC_PWRST_D0); 1016ab6734e7SLydia Wang } 1017ab6734e7SLydia Wang 1018ab6734e7SLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 1019ab6734e7SLydia Wang parm = AC_PWRST_D3; 1020ab6734e7SLydia Wang set_pin_power_state(codec, 0x33, &parm); 1021ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x1d, 0, 1022ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 1023ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x3d, 0, 1024ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 1025ab6734e7SLydia Wang 1026ab6734e7SLydia Wang /* MW9 (21h) */ 1027ab6734e7SLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 1028ab6734e7SLydia Wang snd_hda_codec_write( 1029ab6734e7SLydia Wang codec, 0x21, 0, 1030ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 1031ab6734e7SLydia Wang else 1032ab6734e7SLydia Wang snd_hda_codec_write( 1033ab6734e7SLydia Wang codec, 0x21, 0, 1034ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 1035f5271101SLydia Wang } 1036f5271101SLydia Wang } 1037f5271101SLydia Wang 1038c577b8a1SJoseph Chan /* 1039c577b8a1SJoseph Chan * input MUX handling 1040c577b8a1SJoseph Chan */ 1041c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 1042c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 1043c577b8a1SJoseph Chan { 1044c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1045c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1046c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 1047c577b8a1SJoseph Chan } 1048c577b8a1SJoseph Chan 1049c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 1050c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 1051c577b8a1SJoseph Chan { 1052c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1053c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1054c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 1055c577b8a1SJoseph Chan 1056c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 1057c577b8a1SJoseph Chan return 0; 1058c577b8a1SJoseph Chan } 1059c577b8a1SJoseph Chan 1060c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 1061c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 1062c577b8a1SJoseph Chan { 1063c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1064c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1065c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 1066c577b8a1SJoseph Chan 1067337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 1068337b9d02STakashi Iwai return -EINVAL; 1069a80e6e3cSLydia Wang /* switch to D0 beofre change index */ 1070a80e6e3cSLydia Wang if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, 1071a80e6e3cSLydia Wang AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 1072a80e6e3cSLydia Wang snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 1073a80e6e3cSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 1074a80e6e3cSLydia Wang /* update jack power state */ 1075a80e6e3cSLydia Wang set_jack_power_state(codec); 1076a80e6e3cSLydia Wang 1077c577b8a1SJoseph Chan return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 1078337b9d02STakashi Iwai spec->mux_nids[adc_idx], 1079c577b8a1SJoseph Chan &spec->cur_mux[adc_idx]); 1080c577b8a1SJoseph Chan } 1081c577b8a1SJoseph Chan 10820aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 10830aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 10840aa62aefSHarald Welte { 10850aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 10860aa62aefSHarald Welte struct via_spec *spec = codec->spec; 10870aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 10880aa62aefSHarald Welte } 10890aa62aefSHarald Welte 10900aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 10910aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 10920aa62aefSHarald Welte { 10930aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 10940aa62aefSHarald Welte struct via_spec *spec = codec->spec; 1095eb7188caSLydia Wang hda_nid_t nid; 1096eb7188caSLydia Wang unsigned int pinsel; 1097eb7188caSLydia Wang 1098eb7188caSLydia Wang switch (spec->codec_type) { 1099eb7188caSLydia Wang case VT1718S: 1100eb7188caSLydia Wang nid = 0x34; 1101eb7188caSLydia Wang break; 110225eaba2fSLydia Wang case VT2002P: 110325eaba2fSLydia Wang nid = 0x35; 110425eaba2fSLydia Wang break; 1105ab6734e7SLydia Wang case VT1812: 1106ab6734e7SLydia Wang nid = 0x3d; 1107ab6734e7SLydia Wang break; 1108eb7188caSLydia Wang default: 1109eb7188caSLydia Wang nid = spec->autocfg.hp_pins[0]; 1110eb7188caSLydia Wang break; 1111eb7188caSLydia Wang } 1112eb7188caSLydia Wang /* use !! to translate conn sel 2 for VT1718S */ 1113eb7188caSLydia Wang pinsel = !!snd_hda_codec_read(codec, nid, 0, 11140aa62aefSHarald Welte AC_VERB_GET_CONNECT_SEL, 11150aa62aefSHarald Welte 0x00); 11160aa62aefSHarald Welte ucontrol->value.enumerated.item[0] = pinsel; 11170aa62aefSHarald Welte 11180aa62aefSHarald Welte return 0; 11190aa62aefSHarald Welte } 11200aa62aefSHarald Welte 11210713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active) 11220713efebSLydia Wang { 11230713efebSLydia Wang struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); 11240713efebSLydia Wang if (ctl) { 11250713efebSLydia Wang ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 11260713efebSLydia Wang ctl->vd[0].access |= active 11270713efebSLydia Wang ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; 11280713efebSLydia Wang snd_ctl_notify(codec->bus->card, 11290713efebSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); 11300713efebSLydia Wang } 11310713efebSLydia Wang } 11320713efebSLydia Wang 1133cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec) 1134cdc1784dSLydia Wang { 1135cdc1784dSLydia Wang /* mute side channel */ 1136cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 1137cdc1784dSLydia Wang unsigned int parm = spec->hp_independent_mode 1138cdc1784dSLydia Wang ? AMP_OUT_MUTE : AMP_OUT_UNMUTE; 1139cdc1784dSLydia Wang hda_nid_t sw3; 1140cdc1784dSLydia Wang 1141cdc1784dSLydia Wang switch (spec->codec_type) { 1142cdc1784dSLydia Wang case VT1708: 1143cdc1784dSLydia Wang sw3 = 0x1b; 1144cdc1784dSLydia Wang break; 1145cdc1784dSLydia Wang case VT1709_10CH: 1146cdc1784dSLydia Wang sw3 = 0x29; 1147cdc1784dSLydia Wang break; 1148cdc1784dSLydia Wang case VT1708B_8CH: 1149cdc1784dSLydia Wang case VT1708S: 1150cdc1784dSLydia Wang sw3 = 0x27; 1151cdc1784dSLydia Wang break; 1152cdc1784dSLydia Wang default: 1153cdc1784dSLydia Wang sw3 = 0; 1154cdc1784dSLydia Wang break; 1155cdc1784dSLydia Wang } 1156cdc1784dSLydia Wang 1157cdc1784dSLydia Wang if (sw3) 1158cdc1784dSLydia Wang snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE, 1159cdc1784dSLydia Wang parm); 1160cdc1784dSLydia Wang return 0; 1161cdc1784dSLydia Wang } 1162cdc1784dSLydia Wang 11630aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 11640aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 11650aa62aefSHarald Welte { 11660aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 11670aa62aefSHarald Welte struct via_spec *spec = codec->spec; 11680aa62aefSHarald Welte hda_nid_t nid = spec->autocfg.hp_pins[0]; 11690aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 1170cdc1784dSLydia Wang /* Get Independent Mode index of headphone pin widget */ 1171cdc1784dSLydia Wang spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel 1172cdc1784dSLydia Wang ? 1 : 0; 11730aa62aefSHarald Welte 1174eb7188caSLydia Wang switch (spec->codec_type) { 1175eb7188caSLydia Wang case VT1718S: 1176eb7188caSLydia Wang nid = 0x34; 1177eb7188caSLydia Wang pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */ 1178eb7188caSLydia Wang spec->multiout.num_dacs = 4; 1179eb7188caSLydia Wang break; 118025eaba2fSLydia Wang case VT2002P: 118125eaba2fSLydia Wang nid = 0x35; 118225eaba2fSLydia Wang break; 1183ab6734e7SLydia Wang case VT1812: 1184ab6734e7SLydia Wang nid = 0x3d; 1185ab6734e7SLydia Wang break; 1186eb7188caSLydia Wang default: 1187eb7188caSLydia Wang nid = spec->autocfg.hp_pins[0]; 1188eb7188caSLydia Wang break; 1189eb7188caSLydia Wang } 1190cdc1784dSLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); 11910aa62aefSHarald Welte 1192cdc1784dSLydia Wang if (spec->multiout.hp_nid && spec->multiout.hp_nid 1193cdc1784dSLydia Wang != spec->multiout.dac_nids[HDA_FRONT]) 1194cdc1784dSLydia Wang snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, 11950aa62aefSHarald Welte 0, 0, 0); 11960aa62aefSHarald Welte 1197cdc1784dSLydia Wang update_side_mute_status(codec); 11980713efebSLydia Wang /* update HP volume/swtich active state */ 11990713efebSLydia Wang if (spec->codec_type == VT1708S 1200eb7188caSLydia Wang || spec->codec_type == VT1702 1201f3db423dSLydia Wang || spec->codec_type == VT1718S 120225eaba2fSLydia Wang || spec->codec_type == VT1716S 1203ab6734e7SLydia Wang || spec->codec_type == VT2002P 1204ab6734e7SLydia Wang || spec->codec_type == VT1812) { 12050713efebSLydia Wang activate_ctl(codec, "Headphone Playback Volume", 12060713efebSLydia Wang spec->hp_independent_mode); 12070713efebSLydia Wang activate_ctl(codec, "Headphone Playback Switch", 12080713efebSLydia Wang spec->hp_independent_mode); 12090713efebSLydia Wang } 12100aa62aefSHarald Welte return 0; 12110aa62aefSHarald Welte } 12120aa62aefSHarald Welte 12130aa62aefSHarald Welte static struct snd_kcontrol_new via_hp_mixer[] = { 12140aa62aefSHarald Welte { 12150aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 12160aa62aefSHarald Welte .name = "Independent HP", 12170aa62aefSHarald Welte .count = 1, 12180aa62aefSHarald Welte .info = via_independent_hp_info, 12190aa62aefSHarald Welte .get = via_independent_hp_get, 12200aa62aefSHarald Welte .put = via_independent_hp_put, 12210aa62aefSHarald Welte }, 12220aa62aefSHarald Welte { } /* end */ 12230aa62aefSHarald Welte }; 12240aa62aefSHarald Welte 12251564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 12261564b287SLydia Wang { 12271564b287SLydia Wang int i; 12281564b287SLydia Wang struct snd_ctl_elem_id id; 12291564b287SLydia Wang const char *labels[] = {"Mic", "Front Mic", "Line"}; 12301564b287SLydia Wang 12311564b287SLydia Wang memset(&id, 0, sizeof(id)); 12321564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 12331564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(labels); i++) { 12341564b287SLydia Wang sprintf(id.name, "%s Playback Volume", labels[i]); 12351564b287SLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 12361564b287SLydia Wang &id); 12371564b287SLydia Wang } 12381564b287SLydia Wang } 12391564b287SLydia Wang 12401564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 12411564b287SLydia Wang { 12421564b287SLydia Wang struct via_spec *spec = codec->spec; 12431564b287SLydia Wang hda_nid_t nid_mixer; 12441564b287SLydia Wang int start_idx; 12451564b287SLydia Wang int end_idx; 12461564b287SLydia Wang int i; 12471564b287SLydia Wang /* get nid of MW0 and start & end index */ 12481564b287SLydia Wang switch (spec->codec_type) { 12491564b287SLydia Wang case VT1708: 12501564b287SLydia Wang nid_mixer = 0x17; 12511564b287SLydia Wang start_idx = 2; 12521564b287SLydia Wang end_idx = 4; 12531564b287SLydia Wang break; 12541564b287SLydia Wang case VT1709_10CH: 12551564b287SLydia Wang case VT1709_6CH: 12561564b287SLydia Wang nid_mixer = 0x18; 12571564b287SLydia Wang start_idx = 2; 12581564b287SLydia Wang end_idx = 4; 12591564b287SLydia Wang break; 12601564b287SLydia Wang case VT1708B_8CH: 12611564b287SLydia Wang case VT1708B_4CH: 12621564b287SLydia Wang case VT1708S: 1263f3db423dSLydia Wang case VT1716S: 12641564b287SLydia Wang nid_mixer = 0x16; 12651564b287SLydia Wang start_idx = 2; 12661564b287SLydia Wang end_idx = 4; 12671564b287SLydia Wang break; 12681564b287SLydia Wang default: 12691564b287SLydia Wang return; 12701564b287SLydia Wang } 12711564b287SLydia Wang /* check AA path's mute status */ 12721564b287SLydia Wang for (i = start_idx; i <= end_idx; i++) { 12731564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 12741564b287SLydia Wang snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i, 12751564b287SLydia Wang HDA_AMP_MUTE, val); 12761564b287SLydia Wang } 12771564b287SLydia Wang } 12781564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) 12791564b287SLydia Wang { 12801564b287SLydia Wang int res = 0; 12811564b287SLydia Wang int index; 12821564b287SLydia Wang for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) { 12831564b287SLydia Wang if (pin == spec->autocfg.input_pins[index]) { 12841564b287SLydia Wang res = 1; 12851564b287SLydia Wang break; 12861564b287SLydia Wang } 12871564b287SLydia Wang } 12881564b287SLydia Wang return res; 12891564b287SLydia Wang } 12901564b287SLydia Wang 12911564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol, 12921564b287SLydia Wang struct snd_ctl_elem_info *uinfo) 12931564b287SLydia Wang { 12941564b287SLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 12951564b287SLydia Wang uinfo->count = 1; 12961564b287SLydia Wang uinfo->value.integer.min = 0; 12971564b287SLydia Wang uinfo->value.integer.max = 1; 12981564b287SLydia Wang return 0; 12991564b287SLydia Wang } 13001564b287SLydia Wang 13011564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 13021564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 13031564b287SLydia Wang { 13041564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 13051564b287SLydia Wang struct via_spec *spec = codec->spec; 13061564b287SLydia Wang int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; 13071564b287SLydia Wang int on = 1; 13081564b287SLydia Wang int i; 13091564b287SLydia Wang 13101564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(index); i++) { 13111564b287SLydia Wang hda_nid_t nid = spec->autocfg.input_pins[index[i]]; 13121564b287SLydia Wang if (nid) { 13131564b287SLydia Wang int ctl = 13141564b287SLydia Wang snd_hda_codec_read(codec, nid, 0, 13151564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 13161564b287SLydia Wang 0); 13171564b287SLydia Wang if (i == AUTO_PIN_FRONT_MIC 1318eb7188caSLydia Wang && spec->hp_independent_mode 1319eb7188caSLydia Wang && spec->codec_type != VT1718S) 13201564b287SLydia Wang continue; /* ignore FMic for independent HP */ 13211564b287SLydia Wang if (ctl & AC_PINCTL_IN_EN 13221564b287SLydia Wang && !(ctl & AC_PINCTL_OUT_EN)) 13231564b287SLydia Wang on = 0; 13241564b287SLydia Wang } 13251564b287SLydia Wang } 13261564b287SLydia Wang *ucontrol->value.integer.value = on; 13271564b287SLydia Wang return 0; 13281564b287SLydia Wang } 13291564b287SLydia Wang 13301564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 13311564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 13321564b287SLydia Wang { 13331564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 13341564b287SLydia Wang struct via_spec *spec = codec->spec; 13351564b287SLydia Wang int out_in = *ucontrol->value.integer.value 13361564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 13371564b287SLydia Wang int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; 13381564b287SLydia Wang int i; 13391564b287SLydia Wang 13401564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(index); i++) { 13411564b287SLydia Wang hda_nid_t nid = spec->autocfg.input_pins[index[i]]; 13421564b287SLydia Wang if (i == AUTO_PIN_FRONT_MIC 1343eb7188caSLydia Wang && spec->hp_independent_mode 1344eb7188caSLydia Wang && spec->codec_type != VT1718S) 13451564b287SLydia Wang continue; /* don't retask FMic for independent HP */ 13461564b287SLydia Wang if (nid) { 13471564b287SLydia Wang unsigned int parm = snd_hda_codec_read( 13481564b287SLydia Wang codec, nid, 0, 13491564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 13501564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 13511564b287SLydia Wang parm |= out_in; 13521564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 13531564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 13541564b287SLydia Wang parm); 13551564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 13561564b287SLydia Wang mute_aa_path(codec, 1); 13571564b287SLydia Wang notify_aa_path_ctls(codec); 13581564b287SLydia Wang } 1359eb7188caSLydia Wang if (spec->codec_type == VT1718S) 1360eb7188caSLydia Wang snd_hda_codec_amp_stereo( 1361eb7188caSLydia Wang codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, 1362eb7188caSLydia Wang HDA_AMP_UNMUTE); 13631564b287SLydia Wang } 13641564b287SLydia Wang if (i == AUTO_PIN_FRONT_MIC) { 1365f3db423dSLydia Wang if (spec->codec_type == VT1708S 1366f3db423dSLydia Wang || spec->codec_type == VT1716S) { 13671564b287SLydia Wang /* input = index 1 (AOW3) */ 13681564b287SLydia Wang snd_hda_codec_write( 13691564b287SLydia Wang codec, nid, 0, 13701564b287SLydia Wang AC_VERB_SET_CONNECT_SEL, 1); 13711564b287SLydia Wang snd_hda_codec_amp_stereo( 13721564b287SLydia Wang codec, nid, HDA_OUTPUT, 13731564b287SLydia Wang 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); 13741564b287SLydia Wang } 13751564b287SLydia Wang } 13761564b287SLydia Wang } 13771564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 13781564b287SLydia Wang set_jack_power_state(codec); 13791564b287SLydia Wang return 1; 13801564b287SLydia Wang } 13811564b287SLydia Wang 13821564b287SLydia Wang static struct snd_kcontrol_new via_smart51_mixer[] = { 13831564b287SLydia Wang { 13841564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 13851564b287SLydia Wang .name = "Smart 5.1", 13861564b287SLydia Wang .count = 1, 13871564b287SLydia Wang .info = via_smart51_info, 13881564b287SLydia Wang .get = via_smart51_get, 13891564b287SLydia Wang .put = via_smart51_put, 13901564b287SLydia Wang }, 13911564b287SLydia Wang {} /* end */ 13921564b287SLydia Wang }; 13931564b287SLydia Wang 1394c577b8a1SJoseph Chan /* capture mixer elements */ 1395c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_capture_mixer[] = { 1396c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), 1397c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), 1398c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), 1399c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), 1400c577b8a1SJoseph Chan { 1401c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1402c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 1403c577b8a1SJoseph Chan * So call somewhat different.. 1404c577b8a1SJoseph Chan */ 1405c577b8a1SJoseph Chan /* .name = "Capture Source", */ 1406c577b8a1SJoseph Chan .name = "Input Source", 1407c577b8a1SJoseph Chan .count = 1, 1408c577b8a1SJoseph Chan .info = via_mux_enum_info, 1409c577b8a1SJoseph Chan .get = via_mux_enum_get, 1410c577b8a1SJoseph Chan .put = via_mux_enum_put, 1411c577b8a1SJoseph Chan }, 1412c577b8a1SJoseph Chan { } /* end */ 1413c577b8a1SJoseph Chan }; 1414f5271101SLydia Wang 1415f5271101SLydia Wang /* check AA path's mute statue */ 1416f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec) 1417f5271101SLydia Wang { 1418f5271101SLydia Wang int mute = 1; 1419f5271101SLydia Wang hda_nid_t nid_mixer; 1420f5271101SLydia Wang int start_idx; 1421f5271101SLydia Wang int end_idx; 1422f5271101SLydia Wang int i; 1423f5271101SLydia Wang struct via_spec *spec = codec->spec; 1424f5271101SLydia Wang /* get nid of MW0 and start & end index */ 1425f5271101SLydia Wang switch (spec->codec_type) { 1426f5271101SLydia Wang case VT1708B_8CH: 1427f5271101SLydia Wang case VT1708B_4CH: 1428f5271101SLydia Wang case VT1708S: 1429f3db423dSLydia Wang case VT1716S: 1430f5271101SLydia Wang nid_mixer = 0x16; 1431f5271101SLydia Wang start_idx = 2; 1432f5271101SLydia Wang end_idx = 4; 1433f5271101SLydia Wang break; 1434f5271101SLydia Wang case VT1702: 1435f5271101SLydia Wang nid_mixer = 0x1a; 1436f5271101SLydia Wang start_idx = 1; 1437f5271101SLydia Wang end_idx = 3; 1438f5271101SLydia Wang break; 1439eb7188caSLydia Wang case VT1718S: 1440eb7188caSLydia Wang nid_mixer = 0x21; 1441eb7188caSLydia Wang start_idx = 1; 1442eb7188caSLydia Wang end_idx = 3; 1443eb7188caSLydia Wang break; 144425eaba2fSLydia Wang case VT2002P: 1445ab6734e7SLydia Wang case VT1812: 144625eaba2fSLydia Wang nid_mixer = 0x21; 144725eaba2fSLydia Wang start_idx = 0; 144825eaba2fSLydia Wang end_idx = 2; 144925eaba2fSLydia Wang break; 1450f5271101SLydia Wang default: 1451f5271101SLydia Wang return 0; 1452f5271101SLydia Wang } 1453f5271101SLydia Wang /* check AA path's mute status */ 1454f5271101SLydia Wang for (i = start_idx; i <= end_idx; i++) { 1455f5271101SLydia Wang unsigned int con_list = snd_hda_codec_read( 1456f5271101SLydia Wang codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); 1457f5271101SLydia Wang int shift = 8 * (i % 4); 1458f5271101SLydia Wang hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; 1459f5271101SLydia Wang unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); 1460f5271101SLydia Wang if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { 1461f5271101SLydia Wang /* check mute status while the pin is connected */ 1462f5271101SLydia Wang int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, 1463f5271101SLydia Wang HDA_INPUT, i) >> 7; 1464f5271101SLydia Wang int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, 1465f5271101SLydia Wang HDA_INPUT, i) >> 7; 1466f5271101SLydia Wang if (!mute_l || !mute_r) { 1467f5271101SLydia Wang mute = 0; 1468f5271101SLydia Wang break; 1469f5271101SLydia Wang } 1470f5271101SLydia Wang } 1471f5271101SLydia Wang } 1472f5271101SLydia Wang return mute; 1473f5271101SLydia Wang } 1474f5271101SLydia Wang 1475f5271101SLydia Wang /* enter/exit analog low-current mode */ 1476f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) 1477f5271101SLydia Wang { 1478f5271101SLydia Wang struct via_spec *spec = codec->spec; 1479f5271101SLydia Wang static int saved_stream_idle = 1; /* saved stream idle status */ 1480f5271101SLydia Wang int enable = is_aa_path_mute(codec); 1481f5271101SLydia Wang unsigned int verb = 0; 1482f5271101SLydia Wang unsigned int parm = 0; 1483f5271101SLydia Wang 1484f5271101SLydia Wang if (stream_idle == -1) /* stream status did not change */ 1485f5271101SLydia Wang enable = enable && saved_stream_idle; 1486f5271101SLydia Wang else { 1487f5271101SLydia Wang enable = enable && stream_idle; 1488f5271101SLydia Wang saved_stream_idle = stream_idle; 1489f5271101SLydia Wang } 1490f5271101SLydia Wang 1491f5271101SLydia Wang /* decide low current mode's verb & parameter */ 1492f5271101SLydia Wang switch (spec->codec_type) { 1493f5271101SLydia Wang case VT1708B_8CH: 1494f5271101SLydia Wang case VT1708B_4CH: 1495f5271101SLydia Wang verb = 0xf70; 1496f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 1497f5271101SLydia Wang break; 1498f5271101SLydia Wang case VT1708S: 1499eb7188caSLydia Wang case VT1718S: 1500f3db423dSLydia Wang case VT1716S: 1501f5271101SLydia Wang verb = 0xf73; 1502f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 1503f5271101SLydia Wang break; 1504f5271101SLydia Wang case VT1702: 1505f5271101SLydia Wang verb = 0xf73; 1506f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 1507f5271101SLydia Wang break; 150825eaba2fSLydia Wang case VT2002P: 1509ab6734e7SLydia Wang case VT1812: 151025eaba2fSLydia Wang verb = 0xf93; 151125eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 151225eaba2fSLydia Wang break; 1513f5271101SLydia Wang default: 1514f5271101SLydia Wang return; /* other codecs are not supported */ 1515f5271101SLydia Wang } 1516f5271101SLydia Wang /* send verb */ 1517f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 1518f5271101SLydia Wang } 1519f5271101SLydia Wang 1520c577b8a1SJoseph Chan /* 1521c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1522c577b8a1SJoseph Chan */ 1523c577b8a1SJoseph Chan static struct hda_verb vt1708_volume_init_verbs[] = { 1524c577b8a1SJoseph Chan /* 1525c577b8a1SJoseph Chan * Unmute ADC0-1 and set the default input to mic-in 1526c577b8a1SJoseph Chan */ 1527c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1528c577b8a1SJoseph Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1529c577b8a1SJoseph Chan 1530c577b8a1SJoseph Chan 1531f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1532c577b8a1SJoseph Chan * mixer widget 1533c577b8a1SJoseph Chan */ 1534c577b8a1SJoseph Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1535f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1536f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1537f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1538f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1539f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1540c577b8a1SJoseph Chan 1541c577b8a1SJoseph Chan /* 1542c577b8a1SJoseph Chan * Set up output mixers (0x19 - 0x1b) 1543c577b8a1SJoseph Chan */ 1544c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 1545c577b8a1SJoseph Chan {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1546c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1547c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1548c577b8a1SJoseph Chan 1549bfdc675aSLydia Wang /* Setup default input MW0 to PW4 */ 1550bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 1551c577b8a1SJoseph Chan /* PW9 Output enable */ 1552c577b8a1SJoseph Chan {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1553f7278fd0SJosepch Chan { } 1554c577b8a1SJoseph Chan }; 1555c577b8a1SJoseph Chan 1556c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, 1557c577b8a1SJoseph Chan struct hda_codec *codec, 1558c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1559c577b8a1SJoseph Chan { 1560c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 156117314379SLydia Wang int idle = substream->pstr->substream_opened == 1 156217314379SLydia Wang && substream->ref_count == 0; 156317314379SLydia Wang analog_low_current_mode(codec, idle); 15649a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 15659a08160bSTakashi Iwai hinfo); 1566c577b8a1SJoseph Chan } 1567c577b8a1SJoseph Chan 15680aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec, 15690aa62aefSHarald Welte unsigned int stream_tag, 15700aa62aefSHarald Welte unsigned int format, 15710aa62aefSHarald Welte struct snd_pcm_substream *substream) 15720aa62aefSHarald Welte { 15730aa62aefSHarald Welte struct via_spec *spec = codec->spec; 15740aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 15750aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 15760aa62aefSHarald Welte int chs = substream->runtime->channels; 15770aa62aefSHarald Welte int i; 15780aa62aefSHarald Welte 15790aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 15800aa62aefSHarald Welte if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { 15810aa62aefSHarald Welte if (chs == 2 && 15820aa62aefSHarald Welte snd_hda_is_supported_format(codec, mout->dig_out_nid, 15830aa62aefSHarald Welte format) && 15840aa62aefSHarald Welte !(codec->spdif_status & IEC958_AES0_NONAUDIO)) { 15850aa62aefSHarald Welte mout->dig_out_used = HDA_DIG_ANALOG_DUP; 15860aa62aefSHarald Welte /* turn off SPDIF once; otherwise the IEC958 bits won't 15870aa62aefSHarald Welte * be updated */ 15880aa62aefSHarald Welte if (codec->spdif_ctls & AC_DIG1_ENABLE) 15890aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 15900aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 15910aa62aefSHarald Welte codec->spdif_ctls & 15920aa62aefSHarald Welte ~AC_DIG1_ENABLE & 0xff); 15930aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 15940aa62aefSHarald Welte stream_tag, 0, format); 15950aa62aefSHarald Welte /* turn on again (if needed) */ 15960aa62aefSHarald Welte if (codec->spdif_ctls & AC_DIG1_ENABLE) 15970aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 15980aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 15990aa62aefSHarald Welte codec->spdif_ctls & 0xff); 16000aa62aefSHarald Welte } else { 16010aa62aefSHarald Welte mout->dig_out_used = 0; 16020aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 16030aa62aefSHarald Welte 0, 0, 0); 16040aa62aefSHarald Welte } 16050aa62aefSHarald Welte } 16060aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 16070aa62aefSHarald Welte 16080aa62aefSHarald Welte /* front */ 16090aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 16100aa62aefSHarald Welte 0, format); 16110aa62aefSHarald Welte 1612eb7188caSLydia Wang if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] 1613eb7188caSLydia Wang && !spec->hp_independent_mode) 16140aa62aefSHarald Welte /* headphone out will just decode front left/right (stereo) */ 16150aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 16160aa62aefSHarald Welte 0, format); 16170aa62aefSHarald Welte 16180aa62aefSHarald Welte /* extra outputs copied from front */ 16190aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 16200aa62aefSHarald Welte if (mout->extra_out_nid[i]) 16210aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 16220aa62aefSHarald Welte mout->extra_out_nid[i], 16230aa62aefSHarald Welte stream_tag, 0, format); 16240aa62aefSHarald Welte 16250aa62aefSHarald Welte /* surrounds */ 16260aa62aefSHarald Welte for (i = 1; i < mout->num_dacs; i++) { 16270aa62aefSHarald Welte if (chs >= (i + 1) * 2) /* independent out */ 16280aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 16290aa62aefSHarald Welte i * 2, format); 16300aa62aefSHarald Welte else /* copy front */ 16310aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 16320aa62aefSHarald Welte 0, format); 16330aa62aefSHarald Welte } 16340aa62aefSHarald Welte } 16350aa62aefSHarald Welte 16360aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 16370aa62aefSHarald Welte struct hda_codec *codec, 16380aa62aefSHarald Welte unsigned int stream_tag, 16390aa62aefSHarald Welte unsigned int format, 16400aa62aefSHarald Welte struct snd_pcm_substream *substream) 16410aa62aefSHarald Welte { 16420aa62aefSHarald Welte struct via_spec *spec = codec->spec; 16430aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 16440aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 16450aa62aefSHarald Welte 16460aa62aefSHarald Welte if (substream->number == 0) 16470aa62aefSHarald Welte playback_multi_pcm_prep_0(codec, stream_tag, format, 16480aa62aefSHarald Welte substream); 16490aa62aefSHarald Welte else { 16500aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 16510aa62aefSHarald Welte spec->hp_independent_mode) 16520aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 16530aa62aefSHarald Welte stream_tag, 0, format); 16540aa62aefSHarald Welte } 16551f2e99feSLydia Wang vt1708_start_hp_work(spec); 16560aa62aefSHarald Welte return 0; 16570aa62aefSHarald Welte } 16580aa62aefSHarald Welte 16590aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 16600aa62aefSHarald Welte struct hda_codec *codec, 16610aa62aefSHarald Welte struct snd_pcm_substream *substream) 16620aa62aefSHarald Welte { 16630aa62aefSHarald Welte struct via_spec *spec = codec->spec; 16640aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 16650aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 16660aa62aefSHarald Welte int i; 16670aa62aefSHarald Welte 16680aa62aefSHarald Welte if (substream->number == 0) { 16690aa62aefSHarald Welte for (i = 0; i < mout->num_dacs; i++) 16700aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); 16710aa62aefSHarald Welte 16720aa62aefSHarald Welte if (mout->hp_nid && !spec->hp_independent_mode) 16730aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 16740aa62aefSHarald Welte 0, 0, 0); 16750aa62aefSHarald Welte 16760aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 16770aa62aefSHarald Welte if (mout->extra_out_nid[i]) 16780aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 16790aa62aefSHarald Welte mout->extra_out_nid[i], 16800aa62aefSHarald Welte 0, 0, 0); 16810aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 16820aa62aefSHarald Welte if (mout->dig_out_nid && 16830aa62aefSHarald Welte mout->dig_out_used == HDA_DIG_ANALOG_DUP) { 16840aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 16850aa62aefSHarald Welte 0, 0, 0); 16860aa62aefSHarald Welte mout->dig_out_used = 0; 16870aa62aefSHarald Welte } 16880aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 16890aa62aefSHarald Welte } else { 16900aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 16910aa62aefSHarald Welte spec->hp_independent_mode) 16920aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 16930aa62aefSHarald Welte 0, 0, 0); 16940aa62aefSHarald Welte } 16951f2e99feSLydia Wang vt1708_stop_hp_work(spec); 16960aa62aefSHarald Welte return 0; 16970aa62aefSHarald Welte } 16980aa62aefSHarald Welte 1699c577b8a1SJoseph Chan /* 1700c577b8a1SJoseph Chan * Digital out 1701c577b8a1SJoseph Chan */ 1702c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1703c577b8a1SJoseph Chan struct hda_codec *codec, 1704c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1705c577b8a1SJoseph Chan { 1706c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1707c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1708c577b8a1SJoseph Chan } 1709c577b8a1SJoseph Chan 1710c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1711c577b8a1SJoseph Chan struct hda_codec *codec, 1712c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1713c577b8a1SJoseph Chan { 1714c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1715c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1716c577b8a1SJoseph Chan } 1717c577b8a1SJoseph Chan 17185691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 171998aa34c0SHarald Welte struct hda_codec *codec, 172098aa34c0SHarald Welte unsigned int stream_tag, 172198aa34c0SHarald Welte unsigned int format, 172298aa34c0SHarald Welte struct snd_pcm_substream *substream) 172398aa34c0SHarald Welte { 172498aa34c0SHarald Welte struct via_spec *spec = codec->spec; 17259da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 17269da29271STakashi Iwai stream_tag, format, substream); 17279da29271STakashi Iwai } 17285691ec7fSHarald Welte 17299da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 17309da29271STakashi Iwai struct hda_codec *codec, 17319da29271STakashi Iwai struct snd_pcm_substream *substream) 17329da29271STakashi Iwai { 17339da29271STakashi Iwai struct via_spec *spec = codec->spec; 17349da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 173598aa34c0SHarald Welte return 0; 173698aa34c0SHarald Welte } 173798aa34c0SHarald Welte 1738c577b8a1SJoseph Chan /* 1739c577b8a1SJoseph Chan * Analog capture 1740c577b8a1SJoseph Chan */ 1741c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1742c577b8a1SJoseph Chan struct hda_codec *codec, 1743c577b8a1SJoseph Chan unsigned int stream_tag, 1744c577b8a1SJoseph Chan unsigned int format, 1745c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1746c577b8a1SJoseph Chan { 1747c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1748c577b8a1SJoseph Chan 1749c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1750c577b8a1SJoseph Chan stream_tag, 0, format); 1751c577b8a1SJoseph Chan return 0; 1752c577b8a1SJoseph Chan } 1753c577b8a1SJoseph Chan 1754c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1755c577b8a1SJoseph Chan struct hda_codec *codec, 1756c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1757c577b8a1SJoseph Chan { 1758c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1759888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1760c577b8a1SJoseph Chan return 0; 1761c577b8a1SJoseph Chan } 1762c577b8a1SJoseph Chan 1763c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_playback = { 17640aa62aefSHarald Welte .substreams = 2, 1765c577b8a1SJoseph Chan .channels_min = 2, 1766c577b8a1SJoseph Chan .channels_max = 8, 1767c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 1768c577b8a1SJoseph Chan .ops = { 1769c577b8a1SJoseph Chan .open = via_playback_pcm_open, 17700aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 17710aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1772c577b8a1SJoseph Chan }, 1773c577b8a1SJoseph Chan }; 1774c577b8a1SJoseph Chan 1775bc9b5623STakashi Iwai static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 1776c873cc25SLydia Wang .substreams = 2, 1777bc9b5623STakashi Iwai .channels_min = 2, 1778bc9b5623STakashi Iwai .channels_max = 8, 1779bc9b5623STakashi Iwai .nid = 0x10, /* NID to query formats and rates */ 1780bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1781bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1782bc9b5623STakashi Iwai * disable the 24bit format, so far. 1783bc9b5623STakashi Iwai */ 1784bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1785bc9b5623STakashi Iwai .ops = { 1786bc9b5623STakashi Iwai .open = via_playback_pcm_open, 1787c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1788c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1789bc9b5623STakashi Iwai }, 1790bc9b5623STakashi Iwai }; 1791bc9b5623STakashi Iwai 1792c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_capture = { 1793c577b8a1SJoseph Chan .substreams = 2, 1794c577b8a1SJoseph Chan .channels_min = 2, 1795c577b8a1SJoseph Chan .channels_max = 2, 1796c577b8a1SJoseph Chan .nid = 0x15, /* NID to query formats and rates */ 1797c577b8a1SJoseph Chan .ops = { 1798c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1799c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1800c577b8a1SJoseph Chan }, 1801c577b8a1SJoseph Chan }; 1802c577b8a1SJoseph Chan 1803c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_playback = { 1804c577b8a1SJoseph Chan .substreams = 1, 1805c577b8a1SJoseph Chan .channels_min = 2, 1806c577b8a1SJoseph Chan .channels_max = 2, 1807c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1808c577b8a1SJoseph Chan .ops = { 1809c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 18106b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 18119da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 18129da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1813c577b8a1SJoseph Chan }, 1814c577b8a1SJoseph Chan }; 1815c577b8a1SJoseph Chan 1816c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_capture = { 1817c577b8a1SJoseph Chan .substreams = 1, 1818c577b8a1SJoseph Chan .channels_min = 2, 1819c577b8a1SJoseph Chan .channels_max = 2, 1820c577b8a1SJoseph Chan }; 1821c577b8a1SJoseph Chan 1822c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1823c577b8a1SJoseph Chan { 1824c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1825c577b8a1SJoseph Chan int err; 1826c577b8a1SJoseph Chan int i; 1827c577b8a1SJoseph Chan 1828c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1829c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1830c577b8a1SJoseph Chan if (err < 0) 1831c577b8a1SJoseph Chan return err; 1832c577b8a1SJoseph Chan } 1833c577b8a1SJoseph Chan 1834c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1835c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 1836c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1837c577b8a1SJoseph Chan if (err < 0) 1838c577b8a1SJoseph Chan return err; 18399a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 18409a08160bSTakashi Iwai &spec->multiout); 18419a08160bSTakashi Iwai if (err < 0) 18429a08160bSTakashi Iwai return err; 18439a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1844c577b8a1SJoseph Chan } 1845c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1846c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1847c577b8a1SJoseph Chan if (err < 0) 1848c577b8a1SJoseph Chan return err; 1849c577b8a1SJoseph Chan } 185017314379SLydia Wang 185117314379SLydia Wang /* init power states */ 185217314379SLydia Wang set_jack_power_state(codec); 185317314379SLydia Wang analog_low_current_mode(codec, 1); 185417314379SLydia Wang 1855603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1856c577b8a1SJoseph Chan return 0; 1857c577b8a1SJoseph Chan } 1858c577b8a1SJoseph Chan 1859c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1860c577b8a1SJoseph Chan { 1861c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1862c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1863c577b8a1SJoseph Chan 1864c577b8a1SJoseph Chan codec->num_pcms = 1; 1865c577b8a1SJoseph Chan codec->pcm_info = info; 1866c577b8a1SJoseph Chan 1867c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 1868*377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1869*377ff31aSLydia Wang *(spec->stream_analog_playback); 1870*377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1871*377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1872c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); 1873c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 1874c577b8a1SJoseph Chan 1875c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1876c577b8a1SJoseph Chan spec->multiout.max_channels; 1877c577b8a1SJoseph Chan 1878c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1879c577b8a1SJoseph Chan codec->num_pcms++; 1880c577b8a1SJoseph Chan info++; 1881c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 18827ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1883c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1884c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1885c577b8a1SJoseph Chan *(spec->stream_digital_playback); 1886c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1887c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1888c577b8a1SJoseph Chan } 1889c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1890c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 1891c577b8a1SJoseph Chan *(spec->stream_digital_capture); 1892c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1893c577b8a1SJoseph Chan spec->dig_in_nid; 1894c577b8a1SJoseph Chan } 1895c577b8a1SJoseph Chan } 1896c577b8a1SJoseph Chan 1897c577b8a1SJoseph Chan return 0; 1898c577b8a1SJoseph Chan } 1899c577b8a1SJoseph Chan 1900c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1901c577b8a1SJoseph Chan { 1902c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1903c577b8a1SJoseph Chan 1904c577b8a1SJoseph Chan if (!spec) 1905c577b8a1SJoseph Chan return; 1906c577b8a1SJoseph Chan 1907603c4019STakashi Iwai via_free_kctls(codec); 19081f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1909c577b8a1SJoseph Chan kfree(codec->spec); 1910c577b8a1SJoseph Chan } 1911c577b8a1SJoseph Chan 191269e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 191369e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 191469e52a80SHarald Welte { 1915dcf34c8cSLydia Wang unsigned int present = 0; 191669e52a80SHarald Welte struct via_spec *spec = codec->spec; 191769e52a80SHarald Welte 191869e52a80SHarald Welte present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, 191969e52a80SHarald Welte AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 1920dcf34c8cSLydia Wang 1921dcf34c8cSLydia Wang if (!spec->hp_independent_mode) { 1922dcf34c8cSLydia Wang struct snd_ctl_elem_id id; 1923dcf34c8cSLydia Wang /* auto mute */ 1924dcf34c8cSLydia Wang snd_hda_codec_amp_stereo( 1925dcf34c8cSLydia Wang codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0, 1926dcf34c8cSLydia Wang HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); 1927dcf34c8cSLydia Wang /* notify change */ 1928dcf34c8cSLydia Wang memset(&id, 0, sizeof(id)); 1929dcf34c8cSLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 1930dcf34c8cSLydia Wang strcpy(id.name, "Front Playback Switch"); 1931dcf34c8cSLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 1932dcf34c8cSLydia Wang &id); 1933dcf34c8cSLydia Wang } 193469e52a80SHarald Welte } 193569e52a80SHarald Welte 1936f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */ 1937f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec) 1938f3db423dSLydia Wang { 1939f3db423dSLydia Wang unsigned int hp_present, lineout_present; 1940f3db423dSLydia Wang struct via_spec *spec = codec->spec; 1941f3db423dSLydia Wang 1942f3db423dSLydia Wang if (spec->codec_type != VT1716S) 1943f3db423dSLydia Wang return; 1944f3db423dSLydia Wang 1945f3db423dSLydia Wang lineout_present = snd_hda_codec_read( 1946f3db423dSLydia Wang codec, spec->autocfg.line_out_pins[0], 0, 1947f3db423dSLydia Wang AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 1948f3db423dSLydia Wang 1949f3db423dSLydia Wang /* Mute Mono Out if Line Out is plugged */ 1950f3db423dSLydia Wang if (lineout_present) { 1951f3db423dSLydia Wang snd_hda_codec_amp_stereo( 1952f3db423dSLydia Wang codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); 1953f3db423dSLydia Wang return; 1954f3db423dSLydia Wang } 1955f3db423dSLydia Wang 1956f3db423dSLydia Wang hp_present = snd_hda_codec_read( 1957f3db423dSLydia Wang codec, spec->autocfg.hp_pins[0], 0, 1958f3db423dSLydia Wang AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 1959f3db423dSLydia Wang 1960f3db423dSLydia Wang if (!spec->hp_independent_mode) 1961f3db423dSLydia Wang snd_hda_codec_amp_stereo( 1962f3db423dSLydia Wang codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, 1963f3db423dSLydia Wang hp_present ? HDA_AMP_MUTE : 0); 1964f3db423dSLydia Wang } 1965f3db423dSLydia Wang 196669e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 196769e52a80SHarald Welte { 196869e52a80SHarald Welte unsigned int gpio_data; 196969e52a80SHarald Welte unsigned int vol_counter; 197069e52a80SHarald Welte unsigned int vol; 197169e52a80SHarald Welte unsigned int master_vol; 197269e52a80SHarald Welte 197369e52a80SHarald Welte struct via_spec *spec = codec->spec; 197469e52a80SHarald Welte 197569e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 197669e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 197769e52a80SHarald Welte 197869e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 197969e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 198069e52a80SHarald Welte 198169e52a80SHarald Welte vol = vol_counter & 0x1F; 198269e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 198369e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 198469e52a80SHarald Welte AC_AMP_GET_INPUT); 198569e52a80SHarald Welte 198669e52a80SHarald Welte if (gpio_data == 0x02) { 198769e52a80SHarald Welte /* unmute line out */ 198869e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], 198969e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 199069e52a80SHarald Welte 199169e52a80SHarald Welte if (vol_counter & 0x20) { 199269e52a80SHarald Welte /* decrease volume */ 199369e52a80SHarald Welte if (vol > master_vol) 199469e52a80SHarald Welte vol = master_vol; 199569e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 199669e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 199769e52a80SHarald Welte master_vol-vol); 199869e52a80SHarald Welte } else { 199969e52a80SHarald Welte /* increase volume */ 200069e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 200169e52a80SHarald Welte HDA_AMP_VOLMASK, 200269e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 200369e52a80SHarald Welte (master_vol+vol)); 200469e52a80SHarald Welte } 200569e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 200669e52a80SHarald Welte /* mute line out */ 200769e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 200869e52a80SHarald Welte spec->autocfg.line_out_pins[0], 200969e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 201069e52a80SHarald Welte HDA_AMP_MUTE); 201169e52a80SHarald Welte } 201269e52a80SHarald Welte } 201369e52a80SHarald Welte 201425eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */ 201525eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec) 201625eaba2fSLydia Wang { 201725eaba2fSLydia Wang unsigned int hp_present; 201825eaba2fSLydia Wang struct via_spec *spec = codec->spec; 201925eaba2fSLydia Wang 2020ab6734e7SLydia Wang if (spec->codec_type != VT2002P && spec->codec_type != VT1812) 202125eaba2fSLydia Wang return; 202225eaba2fSLydia Wang 202325eaba2fSLydia Wang hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, 202425eaba2fSLydia Wang AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 202525eaba2fSLydia Wang 202625eaba2fSLydia Wang if (!spec->hp_independent_mode) { 202725eaba2fSLydia Wang struct snd_ctl_elem_id id; 202825eaba2fSLydia Wang snd_hda_codec_amp_stereo( 202925eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0, 203025eaba2fSLydia Wang HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); 203125eaba2fSLydia Wang /* notify change */ 203225eaba2fSLydia Wang memset(&id, 0, sizeof(id)); 203325eaba2fSLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 203425eaba2fSLydia Wang strcpy(id.name, "Speaker Playback Switch"); 203525eaba2fSLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 203625eaba2fSLydia Wang &id); 203725eaba2fSLydia Wang } 203825eaba2fSLydia Wang } 203925eaba2fSLydia Wang 204025eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */ 204125eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec) 204225eaba2fSLydia Wang { 204325eaba2fSLydia Wang unsigned int hp_present, present = 0; 204425eaba2fSLydia Wang struct via_spec *spec = codec->spec; 204525eaba2fSLydia Wang int i; 204625eaba2fSLydia Wang 204725eaba2fSLydia Wang if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) 204825eaba2fSLydia Wang return; 204925eaba2fSLydia Wang 205025eaba2fSLydia Wang hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, 205125eaba2fSLydia Wang AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 205225eaba2fSLydia Wang 205325eaba2fSLydia Wang present = snd_hda_codec_read(codec, spec->autocfg.line_out_pins[0], 0, 205425eaba2fSLydia Wang AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 205525eaba2fSLydia Wang 205625eaba2fSLydia Wang if (!spec->hp_independent_mode) { 205725eaba2fSLydia Wang /* Mute Line-Outs */ 205825eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 205925eaba2fSLydia Wang snd_hda_codec_amp_stereo( 206025eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 206125eaba2fSLydia Wang HDA_OUTPUT, 0, 206225eaba2fSLydia Wang HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); 206325eaba2fSLydia Wang if (hp_present) 206425eaba2fSLydia Wang present = hp_present; 206525eaba2fSLydia Wang } 206625eaba2fSLydia Wang /* Speakers */ 206725eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 206825eaba2fSLydia Wang snd_hda_codec_amp_stereo( 206925eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0, 207025eaba2fSLydia Wang HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); 207125eaba2fSLydia Wang } 207225eaba2fSLydia Wang 207325eaba2fSLydia Wang 207469e52a80SHarald Welte /* unsolicited event for jack sensing */ 207569e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 207669e52a80SHarald Welte unsigned int res) 207769e52a80SHarald Welte { 207869e52a80SHarald Welte res >>= 26; 2079a34df19aSLydia Wang if (res & VIA_HP_EVENT) 208069e52a80SHarald Welte via_hp_automute(codec); 2081a34df19aSLydia Wang if (res & VIA_GPIO_EVENT) 208269e52a80SHarald Welte via_gpio_control(codec); 2083a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 2084a34df19aSLydia Wang set_jack_power_state(codec); 2085f3db423dSLydia Wang if (res & VIA_MONO_EVENT) 2086f3db423dSLydia Wang via_mono_automute(codec); 208725eaba2fSLydia Wang if (res & VIA_SPEAKER_EVENT) 208825eaba2fSLydia Wang via_speaker_automute(codec); 208925eaba2fSLydia Wang if (res & VIA_BIND_HP_EVENT) 209025eaba2fSLydia Wang via_hp_bind_automute(codec); 209169e52a80SHarald Welte } 209269e52a80SHarald Welte 2093c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec) 2094c577b8a1SJoseph Chan { 2095c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 209669e52a80SHarald Welte int i; 209769e52a80SHarald Welte for (i = 0; i < spec->num_iverbs; i++) 209869e52a80SHarald Welte snd_hda_sequence_write(codec, spec->init_verbs[i]); 209969e52a80SHarald Welte 2100518bf3baSLydia Wang spec->codec_type = get_codec_type(codec); 2101518bf3baSLydia Wang if (spec->codec_type == VT1708BCE) 2102518bf3baSLydia Wang spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost 2103518bf3baSLydia Wang same */ 2104f7278fd0SJosepch Chan /* Lydia Add for EAPD enable */ 2105f7278fd0SJosepch Chan if (!spec->dig_in_nid) { /* No Digital In connection */ 210655d1d6c1STakashi Iwai if (spec->dig_in_pin) { 210755d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 2108f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 210912b74c80STakashi Iwai PIN_OUT); 211055d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 2111f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 2112f7278fd0SJosepch Chan } 211312b74c80STakashi Iwai } else /* enable SPDIF-input pin */ 211412b74c80STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 211512b74c80STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 2116f7278fd0SJosepch Chan 21179da29271STakashi Iwai /* assign slave outs */ 21189da29271STakashi Iwai if (spec->slave_dig_outs[0]) 21199da29271STakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 21205691ec7fSHarald Welte 2121c577b8a1SJoseph Chan return 0; 2122c577b8a1SJoseph Chan } 2123c577b8a1SJoseph Chan 21241f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 21251f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 21261f2e99feSLydia Wang { 21271f2e99feSLydia Wang struct via_spec *spec = codec->spec; 21281f2e99feSLydia Wang vt1708_stop_hp_work(spec); 21291f2e99feSLydia Wang return 0; 21301f2e99feSLydia Wang } 21311f2e99feSLydia Wang #endif 21321f2e99feSLydia Wang 2133cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2134cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 2135cb53c626STakashi Iwai { 2136cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 2137cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 2138cb53c626STakashi Iwai } 2139cb53c626STakashi Iwai #endif 2140cb53c626STakashi Iwai 2141c577b8a1SJoseph Chan /* 2142c577b8a1SJoseph Chan */ 2143c577b8a1SJoseph Chan static struct hda_codec_ops via_patch_ops = { 2144c577b8a1SJoseph Chan .build_controls = via_build_controls, 2145c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 2146c577b8a1SJoseph Chan .init = via_init, 2147c577b8a1SJoseph Chan .free = via_free, 21481f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 21491f2e99feSLydia Wang .suspend = via_suspend, 21501f2e99feSLydia Wang #endif 2151cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2152cb53c626STakashi Iwai .check_power_status = via_check_power_status, 2153cb53c626STakashi Iwai #endif 2154c577b8a1SJoseph Chan }; 2155c577b8a1SJoseph Chan 2156c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */ 2157c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec, 2158c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2159c577b8a1SJoseph Chan { 2160c577b8a1SJoseph Chan int i; 2161c577b8a1SJoseph Chan hda_nid_t nid; 2162c577b8a1SJoseph Chan 2163c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; 2164c577b8a1SJoseph Chan 2165c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 2166c577b8a1SJoseph Chan 2167c577b8a1SJoseph Chan for (i = 0; i < 4; i++) { 2168c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2169c577b8a1SJoseph Chan if (nid) { 2170c577b8a1SJoseph Chan /* config dac list */ 2171c577b8a1SJoseph Chan switch (i) { 2172c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2173c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2174c577b8a1SJoseph Chan break; 2175c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2176c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2177c577b8a1SJoseph Chan break; 2178c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2179fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 2180c577b8a1SJoseph Chan break; 2181c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 2182fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x13; 2183c577b8a1SJoseph Chan break; 2184c577b8a1SJoseph Chan } 2185c577b8a1SJoseph Chan } 2186c577b8a1SJoseph Chan } 2187c577b8a1SJoseph Chan 2188c577b8a1SJoseph Chan return 0; 2189c577b8a1SJoseph Chan } 2190c577b8a1SJoseph Chan 2191c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 2192c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, 2193c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2194c577b8a1SJoseph Chan { 2195c577b8a1SJoseph Chan char name[32]; 2196c577b8a1SJoseph Chan static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 21979645c203SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b}; 2198c577b8a1SJoseph Chan int i, err; 2199c577b8a1SJoseph Chan 2200c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 2201c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2202c577b8a1SJoseph Chan 2203c577b8a1SJoseph Chan if (!nid) 2204c577b8a1SJoseph Chan continue; 2205c577b8a1SJoseph Chan 22069645c203SLydia Wang nid_vol = nid_vols[i]; 2207c577b8a1SJoseph Chan 2208c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 2209c577b8a1SJoseph Chan /* Center/LFE */ 2210c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2211c577b8a1SJoseph Chan "Center Playback Volume", 2212f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2213f7278fd0SJosepch Chan HDA_OUTPUT)); 2214c577b8a1SJoseph Chan if (err < 0) 2215c577b8a1SJoseph Chan return err; 2216c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2217c577b8a1SJoseph Chan "LFE Playback Volume", 2218f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2219f7278fd0SJosepch Chan HDA_OUTPUT)); 2220c577b8a1SJoseph Chan if (err < 0) 2221c577b8a1SJoseph Chan return err; 2222c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2223c577b8a1SJoseph Chan "Center Playback Switch", 2224f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2225f7278fd0SJosepch Chan HDA_OUTPUT)); 2226c577b8a1SJoseph Chan if (err < 0) 2227c577b8a1SJoseph Chan return err; 2228c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2229c577b8a1SJoseph Chan "LFE Playback Switch", 2230f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2231f7278fd0SJosepch Chan HDA_OUTPUT)); 2232c577b8a1SJoseph Chan if (err < 0) 2233c577b8a1SJoseph Chan return err; 2234c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT) { 2235c577b8a1SJoseph Chan /* add control to mixer index 0 */ 2236c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2237c577b8a1SJoseph Chan "Master Front Playback Volume", 22389645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2239f7278fd0SJosepch Chan HDA_INPUT)); 2240c577b8a1SJoseph Chan if (err < 0) 2241c577b8a1SJoseph Chan return err; 2242c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2243c577b8a1SJoseph Chan "Master Front Playback Switch", 22449645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2245f7278fd0SJosepch Chan HDA_INPUT)); 2246c577b8a1SJoseph Chan if (err < 0) 2247c577b8a1SJoseph Chan return err; 2248c577b8a1SJoseph Chan 2249c577b8a1SJoseph Chan /* add control to PW3 */ 2250c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2251c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2252f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2253f7278fd0SJosepch Chan HDA_OUTPUT)); 2254c577b8a1SJoseph Chan if (err < 0) 2255c577b8a1SJoseph Chan return err; 2256c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2257c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2258f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2259f7278fd0SJosepch Chan HDA_OUTPUT)); 2260c577b8a1SJoseph Chan if (err < 0) 2261c577b8a1SJoseph Chan return err; 2262c577b8a1SJoseph Chan } else { 2263c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2264c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2265f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2266f7278fd0SJosepch Chan HDA_OUTPUT)); 2267c577b8a1SJoseph Chan if (err < 0) 2268c577b8a1SJoseph Chan return err; 2269c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2270c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2271f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2272f7278fd0SJosepch Chan HDA_OUTPUT)); 2273c577b8a1SJoseph Chan if (err < 0) 2274c577b8a1SJoseph Chan return err; 2275c577b8a1SJoseph Chan } 2276c577b8a1SJoseph Chan } 2277c577b8a1SJoseph Chan 2278c577b8a1SJoseph Chan return 0; 2279c577b8a1SJoseph Chan } 2280c577b8a1SJoseph Chan 22810aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 22820aa62aefSHarald Welte { 22830aa62aefSHarald Welte int i; 22840aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 22850aa62aefSHarald Welte static const char *texts[] = { "OFF", "ON", NULL}; 22860aa62aefSHarald Welte 22870aa62aefSHarald Welte /* for hp mode select */ 22880aa62aefSHarald Welte i = 0; 22890aa62aefSHarald Welte while (texts[i] != NULL) { 22900aa62aefSHarald Welte imux->items[imux->num_items].label = texts[i]; 22910aa62aefSHarald Welte imux->items[imux->num_items].index = i; 22920aa62aefSHarald Welte imux->num_items++; 22930aa62aefSHarald Welte i++; 22940aa62aefSHarald Welte } 22950aa62aefSHarald Welte 22960aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 22970aa62aefSHarald Welte } 22980aa62aefSHarald Welte 2299c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 2300c577b8a1SJoseph Chan { 2301c577b8a1SJoseph Chan int err; 2302c577b8a1SJoseph Chan 2303c577b8a1SJoseph Chan if (!pin) 2304c577b8a1SJoseph Chan return 0; 2305c577b8a1SJoseph Chan 2306c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ 2307cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 2308c577b8a1SJoseph Chan 2309c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2310c577b8a1SJoseph Chan "Headphone Playback Volume", 2311c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2312c577b8a1SJoseph Chan if (err < 0) 2313c577b8a1SJoseph Chan return err; 2314c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2315c577b8a1SJoseph Chan "Headphone Playback Switch", 2316c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2317c577b8a1SJoseph Chan if (err < 0) 2318c577b8a1SJoseph Chan return err; 2319c577b8a1SJoseph Chan 23200aa62aefSHarald Welte create_hp_imux(spec); 23210aa62aefSHarald Welte 2322c577b8a1SJoseph Chan return 0; 2323c577b8a1SJoseph Chan } 2324c577b8a1SJoseph Chan 2325c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 2326c577b8a1SJoseph Chan static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec, 2327c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2328c577b8a1SJoseph Chan { 2329c577b8a1SJoseph Chan static char *labels[] = { 2330c577b8a1SJoseph Chan "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 2331c577b8a1SJoseph Chan }; 23320aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 2333c577b8a1SJoseph Chan int i, err, idx = 0; 2334c577b8a1SJoseph Chan 2335c577b8a1SJoseph Chan /* for internal loopback recording select */ 2336c577b8a1SJoseph Chan imux->items[imux->num_items].label = "Stereo Mixer"; 2337c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 2338c577b8a1SJoseph Chan imux->num_items++; 2339c577b8a1SJoseph Chan 2340c577b8a1SJoseph Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 2341c577b8a1SJoseph Chan if (!cfg->input_pins[i]) 2342c577b8a1SJoseph Chan continue; 2343c577b8a1SJoseph Chan 2344c577b8a1SJoseph Chan switch (cfg->input_pins[i]) { 2345c577b8a1SJoseph Chan case 0x1d: /* Mic */ 2346c577b8a1SJoseph Chan idx = 2; 2347c577b8a1SJoseph Chan break; 2348c577b8a1SJoseph Chan 2349c577b8a1SJoseph Chan case 0x1e: /* Line In */ 2350c577b8a1SJoseph Chan idx = 3; 2351c577b8a1SJoseph Chan break; 2352c577b8a1SJoseph Chan 2353c577b8a1SJoseph Chan case 0x21: /* Front Mic */ 2354c577b8a1SJoseph Chan idx = 4; 2355c577b8a1SJoseph Chan break; 2356c577b8a1SJoseph Chan 2357c577b8a1SJoseph Chan case 0x24: /* CD */ 2358c577b8a1SJoseph Chan idx = 1; 2359c577b8a1SJoseph Chan break; 2360c577b8a1SJoseph Chan } 23619510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x17); 2362c577b8a1SJoseph Chan if (err < 0) 2363c577b8a1SJoseph Chan return err; 2364c577b8a1SJoseph Chan imux->items[imux->num_items].label = labels[i]; 2365c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 2366c577b8a1SJoseph Chan imux->num_items++; 2367c577b8a1SJoseph Chan } 2368c577b8a1SJoseph Chan return 0; 2369c577b8a1SJoseph Chan } 2370c577b8a1SJoseph Chan 2371cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2372cb53c626STakashi Iwai static struct hda_amp_list vt1708_loopbacks[] = { 2373cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 2374cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 2375cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 2376cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 2377cb53c626STakashi Iwai { } /* end */ 2378cb53c626STakashi Iwai }; 2379cb53c626STakashi Iwai #endif 2380cb53c626STakashi Iwai 238176d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 238276d9b0ddSHarald Welte { 238376d9b0ddSHarald Welte unsigned int def_conf; 238476d9b0ddSHarald Welte unsigned char seqassoc; 238576d9b0ddSHarald Welte 23862f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 238776d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 238876d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 238982ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 239082ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 239176d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 23922f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 239376d9b0ddSHarald Welte } 239476d9b0ddSHarald Welte 239576d9b0ddSHarald Welte return; 239676d9b0ddSHarald Welte } 239776d9b0ddSHarald Welte 23981f2e99feSLydia Wang static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, 23991f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 24001f2e99feSLydia Wang { 24011f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 24021f2e99feSLydia Wang struct via_spec *spec = codec->spec; 24031f2e99feSLydia Wang 24041f2e99feSLydia Wang if (spec->codec_type != VT1708) 24051f2e99feSLydia Wang return 0; 24061f2e99feSLydia Wang spec->vt1708_jack_detectect = 24071f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 24081f2e99feSLydia Wang ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect; 24091f2e99feSLydia Wang return 0; 24101f2e99feSLydia Wang } 24111f2e99feSLydia Wang 24121f2e99feSLydia Wang static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, 24131f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 24141f2e99feSLydia Wang { 24151f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 24161f2e99feSLydia Wang struct via_spec *spec = codec->spec; 24171f2e99feSLydia Wang int change; 24181f2e99feSLydia Wang 24191f2e99feSLydia Wang if (spec->codec_type != VT1708) 24201f2e99feSLydia Wang return 0; 24211f2e99feSLydia Wang spec->vt1708_jack_detectect = ucontrol->value.integer.value[0]; 24221f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 24231f2e99feSLydia Wang == !spec->vt1708_jack_detectect; 24241f2e99feSLydia Wang if (spec->vt1708_jack_detectect) { 24251f2e99feSLydia Wang mute_aa_path(codec, 1); 24261f2e99feSLydia Wang notify_aa_path_ctls(codec); 24271f2e99feSLydia Wang } 24281f2e99feSLydia Wang return change; 24291f2e99feSLydia Wang } 24301f2e99feSLydia Wang 24311f2e99feSLydia Wang static struct snd_kcontrol_new vt1708_jack_detectect[] = { 24321f2e99feSLydia Wang { 24331f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 24341f2e99feSLydia Wang .name = "Jack Detect", 24351f2e99feSLydia Wang .count = 1, 24361f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 24371f2e99feSLydia Wang .get = vt1708_jack_detectect_get, 24381f2e99feSLydia Wang .put = vt1708_jack_detectect_put, 24391f2e99feSLydia Wang }, 24401f2e99feSLydia Wang {} /* end */ 24411f2e99feSLydia Wang }; 24421f2e99feSLydia Wang 2443c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec) 2444c577b8a1SJoseph Chan { 2445c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2446c577b8a1SJoseph Chan int err; 2447c577b8a1SJoseph Chan 244876d9b0ddSHarald Welte /* Add HP and CD pin config connect bit re-config action */ 244976d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 245076d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 245176d9b0ddSHarald Welte 2452c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2453c577b8a1SJoseph Chan if (err < 0) 2454c577b8a1SJoseph Chan return err; 2455c577b8a1SJoseph Chan err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg); 2456c577b8a1SJoseph Chan if (err < 0) 2457c577b8a1SJoseph Chan return err; 2458c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2459c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2460c577b8a1SJoseph Chan 2461c577b8a1SJoseph Chan err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg); 2462c577b8a1SJoseph Chan if (err < 0) 2463c577b8a1SJoseph Chan return err; 2464c577b8a1SJoseph Chan err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2465c577b8a1SJoseph Chan if (err < 0) 2466c577b8a1SJoseph Chan return err; 2467c577b8a1SJoseph Chan err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg); 2468c577b8a1SJoseph Chan if (err < 0) 2469c577b8a1SJoseph Chan return err; 24701f2e99feSLydia Wang /* add jack detect on/off control */ 24711f2e99feSLydia Wang err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect); 24721f2e99feSLydia Wang if (err < 0) 24731f2e99feSLydia Wang return err; 2474c577b8a1SJoseph Chan 2475c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2476c577b8a1SJoseph Chan 24770852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2478c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; 247955d1d6c1STakashi Iwai spec->dig_in_pin = VT1708_DIGIN_PIN; 2480c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2481c577b8a1SJoseph Chan spec->dig_in_nid = VT1708_DIGIN_NID; 2482c577b8a1SJoseph Chan 2483603c4019STakashi Iwai if (spec->kctls.list) 2484603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2485c577b8a1SJoseph Chan 248669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; 2487c577b8a1SJoseph Chan 24880aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 24890aa62aefSHarald Welte 2490f8fdd495SHarald Welte if (spec->hp_mux) 24910aa62aefSHarald Welte spec->mixers[spec->num_mixers++] = via_hp_mixer; 2492c577b8a1SJoseph Chan 24931564b287SLydia Wang spec->mixers[spec->num_mixers++] = via_smart51_mixer; 2494c577b8a1SJoseph Chan return 1; 2495c577b8a1SJoseph Chan } 2496c577b8a1SJoseph Chan 2497c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */ 2498c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec) 2499c577b8a1SJoseph Chan { 250025eaba2fSLydia Wang struct via_spec *spec = codec->spec; 250125eaba2fSLydia Wang 2502c577b8a1SJoseph Chan via_init(codec); 2503c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2504c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 2505c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 2506ab6734e7SLydia Wang if (spec->codec_type == VT2002P || spec->codec_type == VT1812) { 250725eaba2fSLydia Wang via_hp_bind_automute(codec); 250825eaba2fSLydia Wang } else { 250925eaba2fSLydia Wang via_hp_automute(codec); 251025eaba2fSLydia Wang via_speaker_automute(codec); 251125eaba2fSLydia Wang } 251225eaba2fSLydia Wang 2513c577b8a1SJoseph Chan return 0; 2514c577b8a1SJoseph Chan } 2515c577b8a1SJoseph Chan 25161f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 25171f2e99feSLydia Wang { 25181f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 25191f2e99feSLydia Wang vt1708_hp_work.work); 25201f2e99feSLydia Wang if (spec->codec_type != VT1708) 25211f2e99feSLydia Wang return; 25221f2e99feSLydia Wang /* if jack state toggled */ 25231f2e99feSLydia Wang if (spec->vt1708_hp_present 25241f2e99feSLydia Wang != (snd_hda_codec_read(spec->codec, spec->autocfg.hp_pins[0], 0, 25251f2e99feSLydia Wang AC_VERB_GET_PIN_SENSE, 0) >> 31)) { 25261f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 25271f2e99feSLydia Wang via_hp_automute(spec->codec); 25281f2e99feSLydia Wang } 25291f2e99feSLydia Wang vt1708_start_hp_work(spec); 25301f2e99feSLydia Wang } 25311f2e99feSLydia Wang 2532337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2533337b9d02STakashi Iwai { 2534337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2535337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2536337b9d02STakashi Iwai unsigned int type; 2537337b9d02STakashi Iwai int i, n; 2538337b9d02STakashi Iwai 2539337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2540337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2541337b9d02STakashi Iwai while (nid) { 2542a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 25431c55d521STakashi Iwai if (type == AC_WID_PIN) 25441c55d521STakashi Iwai break; 2545337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2546337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2547337b9d02STakashi Iwai if (n <= 0) 2548337b9d02STakashi Iwai break; 2549337b9d02STakashi Iwai if (n > 1) { 2550337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2551337b9d02STakashi Iwai break; 2552337b9d02STakashi Iwai } 2553337b9d02STakashi Iwai nid = conn[0]; 2554337b9d02STakashi Iwai } 2555337b9d02STakashi Iwai } 25561c55d521STakashi Iwai return 0; 2557337b9d02STakashi Iwai } 2558337b9d02STakashi Iwai 2559c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2560c577b8a1SJoseph Chan { 2561c577b8a1SJoseph Chan struct via_spec *spec; 2562c577b8a1SJoseph Chan int err; 2563c577b8a1SJoseph Chan 2564c577b8a1SJoseph Chan /* create a codec specific record */ 2565eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 2566c577b8a1SJoseph Chan if (spec == NULL) 2567c577b8a1SJoseph Chan return -ENOMEM; 2568c577b8a1SJoseph Chan 2569c577b8a1SJoseph Chan codec->spec = spec; 2570c577b8a1SJoseph Chan 2571c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 2572c577b8a1SJoseph Chan err = vt1708_parse_auto_config(codec); 2573c577b8a1SJoseph Chan if (err < 0) { 2574c577b8a1SJoseph Chan via_free(codec); 2575c577b8a1SJoseph Chan return err; 2576c577b8a1SJoseph Chan } else if (!err) { 2577c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2578c577b8a1SJoseph Chan "from BIOS. Using genenic mode...\n"); 2579c577b8a1SJoseph Chan } 2580c577b8a1SJoseph Chan 2581c577b8a1SJoseph Chan 2582c577b8a1SJoseph Chan spec->stream_name_analog = "VT1708 Analog"; 2583c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1708_pcm_analog_playback; 2584bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2585bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2586bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2587c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1708_pcm_analog_capture; 2588c577b8a1SJoseph Chan 2589c577b8a1SJoseph Chan spec->stream_name_digital = "VT1708 Digital"; 2590c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1708_pcm_digital_playback; 2591c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1708_pcm_digital_capture; 2592c577b8a1SJoseph Chan 2593c577b8a1SJoseph Chan 2594c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2595c577b8a1SJoseph Chan spec->adc_nids = vt1708_adc_nids; 2596c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); 25970f67a611STakashi Iwai get_mux_nids(codec); 2598c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1708_capture_mixer; 2599c577b8a1SJoseph Chan spec->num_mixers++; 2600c577b8a1SJoseph Chan } 2601c577b8a1SJoseph Chan 2602c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2603c577b8a1SJoseph Chan 2604c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 2605cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2606cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 2607cb53c626STakashi Iwai #endif 26081f2e99feSLydia Wang spec->codec = codec; 26091f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2610c577b8a1SJoseph Chan return 0; 2611c577b8a1SJoseph Chan } 2612c577b8a1SJoseph Chan 2613c577b8a1SJoseph Chan /* capture mixer elements */ 2614c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1709_capture_mixer[] = { 2615c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), 2616c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), 2617c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), 2618c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), 2619c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), 2620c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), 2621c577b8a1SJoseph Chan { 2622c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2623c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 2624c577b8a1SJoseph Chan * So call somewhat different.. 2625c577b8a1SJoseph Chan */ 2626c577b8a1SJoseph Chan /* .name = "Capture Source", */ 2627c577b8a1SJoseph Chan .name = "Input Source", 2628c577b8a1SJoseph Chan .count = 1, 2629c577b8a1SJoseph Chan .info = via_mux_enum_info, 2630c577b8a1SJoseph Chan .get = via_mux_enum_get, 2631c577b8a1SJoseph Chan .put = via_mux_enum_put, 2632c577b8a1SJoseph Chan }, 2633c577b8a1SJoseph Chan { } /* end */ 2634c577b8a1SJoseph Chan }; 2635c577b8a1SJoseph Chan 263669e52a80SHarald Welte static struct hda_verb vt1709_uniwill_init_verbs[] = { 2637a34df19aSLydia Wang {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 2638a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 263969e52a80SHarald Welte { } 264069e52a80SHarald Welte }; 264169e52a80SHarald Welte 2642c577b8a1SJoseph Chan /* 2643c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2644c577b8a1SJoseph Chan */ 2645c577b8a1SJoseph Chan static struct hda_verb vt1709_10ch_volume_init_verbs[] = { 2646c577b8a1SJoseph Chan /* 2647c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2648c577b8a1SJoseph Chan */ 2649c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2650c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2651c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2652c577b8a1SJoseph Chan 2653c577b8a1SJoseph Chan 2654f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2655c577b8a1SJoseph Chan * mixer widget 2656c577b8a1SJoseph Chan */ 2657c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2658f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2659f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2660f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2661f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2662f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2663c577b8a1SJoseph Chan 2664c577b8a1SJoseph Chan /* 2665c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2666c577b8a1SJoseph Chan */ 2667c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2668c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2669c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2670c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2671c577b8a1SJoseph Chan 2672c577b8a1SJoseph Chan /* 2673c577b8a1SJoseph Chan * Unmute PW3 and PW4 2674c577b8a1SJoseph Chan */ 2675c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2676c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2677c577b8a1SJoseph Chan 2678bfdc675aSLydia Wang /* Set input of PW4 as MW0 */ 2679bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2680c577b8a1SJoseph Chan /* PW9 Output enable */ 2681c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2682c577b8a1SJoseph Chan { } 2683c577b8a1SJoseph Chan }; 2684c577b8a1SJoseph Chan 2685c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { 2686c577b8a1SJoseph Chan .substreams = 1, 2687c577b8a1SJoseph Chan .channels_min = 2, 2688c577b8a1SJoseph Chan .channels_max = 10, 2689c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2690c577b8a1SJoseph Chan .ops = { 2691c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2692c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2693c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2694c577b8a1SJoseph Chan }, 2695c577b8a1SJoseph Chan }; 2696c577b8a1SJoseph Chan 2697c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { 2698c577b8a1SJoseph Chan .substreams = 1, 2699c577b8a1SJoseph Chan .channels_min = 2, 2700c577b8a1SJoseph Chan .channels_max = 6, 2701c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2702c577b8a1SJoseph Chan .ops = { 2703c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2704c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2705c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2706c577b8a1SJoseph Chan }, 2707c577b8a1SJoseph Chan }; 2708c577b8a1SJoseph Chan 2709c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_analog_capture = { 2710c577b8a1SJoseph Chan .substreams = 2, 2711c577b8a1SJoseph Chan .channels_min = 2, 2712c577b8a1SJoseph Chan .channels_max = 2, 2713c577b8a1SJoseph Chan .nid = 0x14, /* NID to query formats and rates */ 2714c577b8a1SJoseph Chan .ops = { 2715c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 2716c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 2717c577b8a1SJoseph Chan }, 2718c577b8a1SJoseph Chan }; 2719c577b8a1SJoseph Chan 2720c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_playback = { 2721c577b8a1SJoseph Chan .substreams = 1, 2722c577b8a1SJoseph Chan .channels_min = 2, 2723c577b8a1SJoseph Chan .channels_max = 2, 2724c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 2725c577b8a1SJoseph Chan .ops = { 2726c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 2727c577b8a1SJoseph Chan .close = via_dig_playback_pcm_close 2728c577b8a1SJoseph Chan }, 2729c577b8a1SJoseph Chan }; 2730c577b8a1SJoseph Chan 2731c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_capture = { 2732c577b8a1SJoseph Chan .substreams = 1, 2733c577b8a1SJoseph Chan .channels_min = 2, 2734c577b8a1SJoseph Chan .channels_max = 2, 2735c577b8a1SJoseph Chan }; 2736c577b8a1SJoseph Chan 2737c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec, 2738c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2739c577b8a1SJoseph Chan { 2740c577b8a1SJoseph Chan int i; 2741c577b8a1SJoseph Chan hda_nid_t nid; 2742c577b8a1SJoseph Chan 2743c577b8a1SJoseph Chan if (cfg->line_outs == 4) /* 10 channels */ 2744c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */ 2745c577b8a1SJoseph Chan else if (cfg->line_outs == 3) /* 6 channels */ 2746c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */ 2747c577b8a1SJoseph Chan 2748c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 2749c577b8a1SJoseph Chan 2750c577b8a1SJoseph Chan if (cfg->line_outs == 4) { /* 10 channels */ 2751c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 2752c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2753c577b8a1SJoseph Chan if (nid) { 2754c577b8a1SJoseph Chan /* config dac list */ 2755c577b8a1SJoseph Chan switch (i) { 2756c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2757c577b8a1SJoseph Chan /* AOW0 */ 2758c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2759c577b8a1SJoseph Chan break; 2760c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2761c577b8a1SJoseph Chan /* AOW2 */ 2762c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2763c577b8a1SJoseph Chan break; 2764c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2765c577b8a1SJoseph Chan /* AOW3 */ 2766fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 2767c577b8a1SJoseph Chan break; 2768c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 2769c577b8a1SJoseph Chan /* AOW1 */ 2770fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x27; 2771c577b8a1SJoseph Chan break; 2772c577b8a1SJoseph Chan default: 2773c577b8a1SJoseph Chan break; 2774c577b8a1SJoseph Chan } 2775c577b8a1SJoseph Chan } 2776c577b8a1SJoseph Chan } 2777c577b8a1SJoseph Chan spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ 2778c577b8a1SJoseph Chan 2779c577b8a1SJoseph Chan } else if (cfg->line_outs == 3) { /* 6 channels */ 2780c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 2781c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2782c577b8a1SJoseph Chan if (nid) { 2783c577b8a1SJoseph Chan /* config dac list */ 2784c577b8a1SJoseph Chan switch (i) { 2785c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2786c577b8a1SJoseph Chan /* AOW0 */ 2787c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2788c577b8a1SJoseph Chan break; 2789c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2790c577b8a1SJoseph Chan /* AOW2 */ 2791c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2792c577b8a1SJoseph Chan break; 2793c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2794c577b8a1SJoseph Chan /* AOW1 */ 2795c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x11; 2796c577b8a1SJoseph Chan break; 2797c577b8a1SJoseph Chan default: 2798c577b8a1SJoseph Chan break; 2799c577b8a1SJoseph Chan } 2800c577b8a1SJoseph Chan } 2801c577b8a1SJoseph Chan } 2802c577b8a1SJoseph Chan } 2803c577b8a1SJoseph Chan 2804c577b8a1SJoseph Chan return 0; 2805c577b8a1SJoseph Chan } 2806c577b8a1SJoseph Chan 2807c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 2808c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, 2809c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2810c577b8a1SJoseph Chan { 2811c577b8a1SJoseph Chan char name[32]; 2812c577b8a1SJoseph Chan static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 28134483a2f5SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29}; 2814c577b8a1SJoseph Chan int i, err; 2815c577b8a1SJoseph Chan 2816c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 2817c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2818c577b8a1SJoseph Chan 2819c577b8a1SJoseph Chan if (!nid) 2820c577b8a1SJoseph Chan continue; 2821c577b8a1SJoseph Chan 28224483a2f5SLydia Wang nid_vol = nid_vols[i]; 28234483a2f5SLydia Wang 2824c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 2825c577b8a1SJoseph Chan /* Center/LFE */ 2826c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2827c577b8a1SJoseph Chan "Center Playback Volume", 28284483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2829f7278fd0SJosepch Chan HDA_OUTPUT)); 2830c577b8a1SJoseph Chan if (err < 0) 2831c577b8a1SJoseph Chan return err; 2832c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2833c577b8a1SJoseph Chan "LFE Playback Volume", 28344483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2835f7278fd0SJosepch Chan HDA_OUTPUT)); 2836c577b8a1SJoseph Chan if (err < 0) 2837c577b8a1SJoseph Chan return err; 2838c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2839c577b8a1SJoseph Chan "Center Playback Switch", 28404483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2841f7278fd0SJosepch Chan HDA_OUTPUT)); 2842c577b8a1SJoseph Chan if (err < 0) 2843c577b8a1SJoseph Chan return err; 2844c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2845c577b8a1SJoseph Chan "LFE Playback Switch", 28464483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2847f7278fd0SJosepch Chan HDA_OUTPUT)); 2848c577b8a1SJoseph Chan if (err < 0) 2849c577b8a1SJoseph Chan return err; 2850c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT) { 28514483a2f5SLydia Wang /* ADD control to mixer index 0 */ 2852c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2853c577b8a1SJoseph Chan "Master Front Playback Volume", 28544483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2855f7278fd0SJosepch Chan HDA_INPUT)); 2856c577b8a1SJoseph Chan if (err < 0) 2857c577b8a1SJoseph Chan return err; 2858c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2859c577b8a1SJoseph Chan "Master Front Playback Switch", 28604483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2861f7278fd0SJosepch Chan HDA_INPUT)); 2862c577b8a1SJoseph Chan if (err < 0) 2863c577b8a1SJoseph Chan return err; 2864c577b8a1SJoseph Chan 2865c577b8a1SJoseph Chan /* add control to PW3 */ 2866c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2867c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2868f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2869f7278fd0SJosepch Chan HDA_OUTPUT)); 2870c577b8a1SJoseph Chan if (err < 0) 2871c577b8a1SJoseph Chan return err; 2872c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2873c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2874f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2875f7278fd0SJosepch Chan HDA_OUTPUT)); 2876c577b8a1SJoseph Chan if (err < 0) 2877c577b8a1SJoseph Chan return err; 2878c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SURROUND) { 2879c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2880c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 28814483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2882f7278fd0SJosepch Chan HDA_OUTPUT)); 2883c577b8a1SJoseph Chan if (err < 0) 2884c577b8a1SJoseph Chan return err; 2885c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2886c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 28874483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2888f7278fd0SJosepch Chan HDA_OUTPUT)); 2889c577b8a1SJoseph Chan if (err < 0) 2890c577b8a1SJoseph Chan return err; 2891c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SIDE) { 2892c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2893c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 28944483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2895f7278fd0SJosepch Chan HDA_OUTPUT)); 2896c577b8a1SJoseph Chan if (err < 0) 2897c577b8a1SJoseph Chan return err; 2898c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2899c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 29004483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2901f7278fd0SJosepch Chan HDA_OUTPUT)); 2902c577b8a1SJoseph Chan if (err < 0) 2903c577b8a1SJoseph Chan return err; 2904c577b8a1SJoseph Chan } 2905c577b8a1SJoseph Chan } 2906c577b8a1SJoseph Chan 2907c577b8a1SJoseph Chan return 0; 2908c577b8a1SJoseph Chan } 2909c577b8a1SJoseph Chan 2910c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 2911c577b8a1SJoseph Chan { 2912c577b8a1SJoseph Chan int err; 2913c577b8a1SJoseph Chan 2914c577b8a1SJoseph Chan if (!pin) 2915c577b8a1SJoseph Chan return 0; 2916c577b8a1SJoseph Chan 2917c577b8a1SJoseph Chan if (spec->multiout.num_dacs == 5) /* 10 channels */ 2918c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1709_HP_DAC_NID; 2919c577b8a1SJoseph Chan else if (spec->multiout.num_dacs == 3) /* 6 channels */ 2920c577b8a1SJoseph Chan spec->multiout.hp_nid = 0; 2921cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 2922c577b8a1SJoseph Chan 2923c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2924c577b8a1SJoseph Chan "Headphone Playback Volume", 2925c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2926c577b8a1SJoseph Chan if (err < 0) 2927c577b8a1SJoseph Chan return err; 2928c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2929c577b8a1SJoseph Chan "Headphone Playback Switch", 2930c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2931c577b8a1SJoseph Chan if (err < 0) 2932c577b8a1SJoseph Chan return err; 2933c577b8a1SJoseph Chan 2934c577b8a1SJoseph Chan return 0; 2935c577b8a1SJoseph Chan } 2936c577b8a1SJoseph Chan 2937c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 2938c577b8a1SJoseph Chan static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec, 2939c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2940c577b8a1SJoseph Chan { 2941c577b8a1SJoseph Chan static char *labels[] = { 2942c577b8a1SJoseph Chan "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 2943c577b8a1SJoseph Chan }; 29440aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 2945c577b8a1SJoseph Chan int i, err, idx = 0; 2946c577b8a1SJoseph Chan 2947c577b8a1SJoseph Chan /* for internal loopback recording select */ 2948c577b8a1SJoseph Chan imux->items[imux->num_items].label = "Stereo Mixer"; 2949c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 2950c577b8a1SJoseph Chan imux->num_items++; 2951c577b8a1SJoseph Chan 2952c577b8a1SJoseph Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 2953c577b8a1SJoseph Chan if (!cfg->input_pins[i]) 2954c577b8a1SJoseph Chan continue; 2955c577b8a1SJoseph Chan 2956c577b8a1SJoseph Chan switch (cfg->input_pins[i]) { 2957c577b8a1SJoseph Chan case 0x1d: /* Mic */ 2958c577b8a1SJoseph Chan idx = 2; 2959c577b8a1SJoseph Chan break; 2960c577b8a1SJoseph Chan 2961c577b8a1SJoseph Chan case 0x1e: /* Line In */ 2962c577b8a1SJoseph Chan idx = 3; 2963c577b8a1SJoseph Chan break; 2964c577b8a1SJoseph Chan 2965c577b8a1SJoseph Chan case 0x21: /* Front Mic */ 2966c577b8a1SJoseph Chan idx = 4; 2967c577b8a1SJoseph Chan break; 2968c577b8a1SJoseph Chan 2969c577b8a1SJoseph Chan case 0x23: /* CD */ 2970c577b8a1SJoseph Chan idx = 1; 2971c577b8a1SJoseph Chan break; 2972c577b8a1SJoseph Chan } 29739510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x18); 2974c577b8a1SJoseph Chan if (err < 0) 2975c577b8a1SJoseph Chan return err; 2976c577b8a1SJoseph Chan imux->items[imux->num_items].label = labels[i]; 2977c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 2978c577b8a1SJoseph Chan imux->num_items++; 2979c577b8a1SJoseph Chan } 2980c577b8a1SJoseph Chan return 0; 2981c577b8a1SJoseph Chan } 2982c577b8a1SJoseph Chan 2983c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec) 2984c577b8a1SJoseph Chan { 2985c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2986c577b8a1SJoseph Chan int err; 2987c577b8a1SJoseph Chan 2988c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2989c577b8a1SJoseph Chan if (err < 0) 2990c577b8a1SJoseph Chan return err; 2991c577b8a1SJoseph Chan err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg); 2992c577b8a1SJoseph Chan if (err < 0) 2993c577b8a1SJoseph Chan return err; 2994c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2995c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2996c577b8a1SJoseph Chan 2997c577b8a1SJoseph Chan err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg); 2998c577b8a1SJoseph Chan if (err < 0) 2999c577b8a1SJoseph Chan return err; 3000c577b8a1SJoseph Chan err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3001c577b8a1SJoseph Chan if (err < 0) 3002c577b8a1SJoseph Chan return err; 3003c577b8a1SJoseph Chan err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg); 3004c577b8a1SJoseph Chan if (err < 0) 3005c577b8a1SJoseph Chan return err; 3006c577b8a1SJoseph Chan 3007c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3008c577b8a1SJoseph Chan 30090852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 3010c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; 301155d1d6c1STakashi Iwai spec->dig_in_pin = VT1709_DIGIN_PIN; 3012c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 3013c577b8a1SJoseph Chan spec->dig_in_nid = VT1709_DIGIN_NID; 3014c577b8a1SJoseph Chan 3015603c4019STakashi Iwai if (spec->kctls.list) 3016603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3017c577b8a1SJoseph Chan 30180aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 3019c577b8a1SJoseph Chan 3020f8fdd495SHarald Welte if (spec->hp_mux) 3021f8fdd495SHarald Welte spec->mixers[spec->num_mixers++] = via_hp_mixer; 3022f8fdd495SHarald Welte 30231564b287SLydia Wang spec->mixers[spec->num_mixers++] = via_smart51_mixer; 3024c577b8a1SJoseph Chan return 1; 3025c577b8a1SJoseph Chan } 3026c577b8a1SJoseph Chan 3027cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 3028cb53c626STakashi Iwai static struct hda_amp_list vt1709_loopbacks[] = { 3029cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 3030cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 3031cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 3032cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 3033cb53c626STakashi Iwai { } /* end */ 3034cb53c626STakashi Iwai }; 3035cb53c626STakashi Iwai #endif 3036cb53c626STakashi Iwai 3037c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 3038c577b8a1SJoseph Chan { 3039c577b8a1SJoseph Chan struct via_spec *spec; 3040c577b8a1SJoseph Chan int err; 3041c577b8a1SJoseph Chan 3042c577b8a1SJoseph Chan /* create a codec specific record */ 3043eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 3044c577b8a1SJoseph Chan if (spec == NULL) 3045c577b8a1SJoseph Chan return -ENOMEM; 3046c577b8a1SJoseph Chan 3047c577b8a1SJoseph Chan codec->spec = spec; 3048c577b8a1SJoseph Chan 3049c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 3050c577b8a1SJoseph Chan if (err < 0) { 3051c577b8a1SJoseph Chan via_free(codec); 3052c577b8a1SJoseph Chan return err; 3053c577b8a1SJoseph Chan } else if (!err) { 3054c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 3055c577b8a1SJoseph Chan "Using genenic mode...\n"); 3056c577b8a1SJoseph Chan } 3057c577b8a1SJoseph Chan 305869e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; 305969e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 3060c577b8a1SJoseph Chan 3061c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 3062c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; 3063c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 3064c577b8a1SJoseph Chan 3065c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 3066c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 3067c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 3068c577b8a1SJoseph Chan 3069c577b8a1SJoseph Chan 3070c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 3071c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 3072c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 3073337b9d02STakashi Iwai get_mux_nids(codec); 3074c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 3075c577b8a1SJoseph Chan spec->num_mixers++; 3076c577b8a1SJoseph Chan } 3077c577b8a1SJoseph Chan 3078c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 3079c577b8a1SJoseph Chan 3080c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 308169e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3082cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 3083cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 3084cb53c626STakashi Iwai #endif 3085c577b8a1SJoseph Chan 3086c577b8a1SJoseph Chan return 0; 3087c577b8a1SJoseph Chan } 3088c577b8a1SJoseph Chan /* 3089c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 3090c577b8a1SJoseph Chan */ 3091c577b8a1SJoseph Chan static struct hda_verb vt1709_6ch_volume_init_verbs[] = { 3092c577b8a1SJoseph Chan /* 3093c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 3094c577b8a1SJoseph Chan */ 3095c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3096c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3097c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3098c577b8a1SJoseph Chan 3099c577b8a1SJoseph Chan 3100c577b8a1SJoseph Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3101c577b8a1SJoseph Chan * mixer widget 3102c577b8a1SJoseph Chan */ 3103c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3104c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3105c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3106c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3107c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3108c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3109c577b8a1SJoseph Chan 3110c577b8a1SJoseph Chan /* 3111c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 3112c577b8a1SJoseph Chan */ 3113c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 3114c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3115c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3116c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3117c577b8a1SJoseph Chan 3118c577b8a1SJoseph Chan /* 3119c577b8a1SJoseph Chan * Unmute PW3 and PW4 3120c577b8a1SJoseph Chan */ 3121c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3122c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3123c577b8a1SJoseph Chan 3124c577b8a1SJoseph Chan /* Set input of PW4 as MW0 */ 3125c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 3126c577b8a1SJoseph Chan /* PW9 Output enable */ 3127c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3128c577b8a1SJoseph Chan { } 3129c577b8a1SJoseph Chan }; 3130c577b8a1SJoseph Chan 3131c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 3132c577b8a1SJoseph Chan { 3133c577b8a1SJoseph Chan struct via_spec *spec; 3134c577b8a1SJoseph Chan int err; 3135c577b8a1SJoseph Chan 3136c577b8a1SJoseph Chan /* create a codec specific record */ 3137eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 3138c577b8a1SJoseph Chan if (spec == NULL) 3139c577b8a1SJoseph Chan return -ENOMEM; 3140c577b8a1SJoseph Chan 3141c577b8a1SJoseph Chan codec->spec = spec; 3142c577b8a1SJoseph Chan 3143c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 3144c577b8a1SJoseph Chan if (err < 0) { 3145c577b8a1SJoseph Chan via_free(codec); 3146c577b8a1SJoseph Chan return err; 3147c577b8a1SJoseph Chan } else if (!err) { 3148c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 3149c577b8a1SJoseph Chan "Using genenic mode...\n"); 3150c577b8a1SJoseph Chan } 3151c577b8a1SJoseph Chan 315269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; 315369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 3154c577b8a1SJoseph Chan 3155c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 3156c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; 3157c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 3158c577b8a1SJoseph Chan 3159c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 3160c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 3161c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 3162c577b8a1SJoseph Chan 3163c577b8a1SJoseph Chan 3164c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 3165c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 3166c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 3167337b9d02STakashi Iwai get_mux_nids(codec); 3168c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 3169c577b8a1SJoseph Chan spec->num_mixers++; 3170c577b8a1SJoseph Chan } 3171c577b8a1SJoseph Chan 3172c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 3173c577b8a1SJoseph Chan 3174c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 317569e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3176cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 3177cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 3178cb53c626STakashi Iwai #endif 3179f7278fd0SJosepch Chan return 0; 3180f7278fd0SJosepch Chan } 3181f7278fd0SJosepch Chan 3182f7278fd0SJosepch Chan /* capture mixer elements */ 3183f7278fd0SJosepch Chan static struct snd_kcontrol_new vt1708B_capture_mixer[] = { 3184f7278fd0SJosepch Chan HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3185f7278fd0SJosepch Chan HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3186f7278fd0SJosepch Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3187f7278fd0SJosepch Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 3188f7278fd0SJosepch Chan { 3189f7278fd0SJosepch Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3190f7278fd0SJosepch Chan /* The multiple "Capture Source" controls confuse alsamixer 3191f7278fd0SJosepch Chan * So call somewhat different.. 3192f7278fd0SJosepch Chan */ 3193f7278fd0SJosepch Chan /* .name = "Capture Source", */ 3194f7278fd0SJosepch Chan .name = "Input Source", 3195f7278fd0SJosepch Chan .count = 1, 3196f7278fd0SJosepch Chan .info = via_mux_enum_info, 3197f7278fd0SJosepch Chan .get = via_mux_enum_get, 3198f7278fd0SJosepch Chan .put = via_mux_enum_put, 3199f7278fd0SJosepch Chan }, 3200f7278fd0SJosepch Chan { } /* end */ 3201f7278fd0SJosepch Chan }; 3202f7278fd0SJosepch Chan /* 3203f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 3204f7278fd0SJosepch Chan */ 3205f7278fd0SJosepch Chan static struct hda_verb vt1708B_8ch_volume_init_verbs[] = { 3206f7278fd0SJosepch Chan /* 3207f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 3208f7278fd0SJosepch Chan */ 3209f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3210f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3211f7278fd0SJosepch Chan 3212f7278fd0SJosepch Chan 3213f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3214f7278fd0SJosepch Chan * mixer widget 3215f7278fd0SJosepch Chan */ 3216f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3217f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3218f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3219f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3220f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3221f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3222f7278fd0SJosepch Chan 3223f7278fd0SJosepch Chan /* 3224f7278fd0SJosepch Chan * Set up output mixers 3225f7278fd0SJosepch Chan */ 3226f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 3227f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3228f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3229f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3230f7278fd0SJosepch Chan 3231f7278fd0SJosepch Chan /* Setup default input to PW4 */ 3232bfdc675aSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, 3233f7278fd0SJosepch Chan /* PW9 Output enable */ 3234f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3235f7278fd0SJosepch Chan /* PW10 Input enable */ 3236f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 3237f7278fd0SJosepch Chan { } 3238f7278fd0SJosepch Chan }; 3239f7278fd0SJosepch Chan 3240f7278fd0SJosepch Chan static struct hda_verb vt1708B_4ch_volume_init_verbs[] = { 3241f7278fd0SJosepch Chan /* 3242f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 3243f7278fd0SJosepch Chan */ 3244f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3245f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3246f7278fd0SJosepch Chan 3247f7278fd0SJosepch Chan 3248f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3249f7278fd0SJosepch Chan * mixer widget 3250f7278fd0SJosepch Chan */ 3251f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3252f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3253f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3254f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3255f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3256f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3257f7278fd0SJosepch Chan 3258f7278fd0SJosepch Chan /* 3259f7278fd0SJosepch Chan * Set up output mixers 3260f7278fd0SJosepch Chan */ 3261f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 3262f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3263f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3264f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3265f7278fd0SJosepch Chan 3266f7278fd0SJosepch Chan /* Setup default input of PW4 to MW0 */ 3267f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 3268f7278fd0SJosepch Chan /* PW9 Output enable */ 3269f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3270f7278fd0SJosepch Chan /* PW10 Input enable */ 3271f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 3272f7278fd0SJosepch Chan { } 3273f7278fd0SJosepch Chan }; 3274f7278fd0SJosepch Chan 327569e52a80SHarald Welte static struct hda_verb vt1708B_uniwill_init_verbs[] = { 3276a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3277a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3278a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3279a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3280a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3281a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3282a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3283a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3284a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 328569e52a80SHarald Welte { } 328669e52a80SHarald Welte }; 328769e52a80SHarald Welte 328817314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo, 328917314379SLydia Wang struct hda_codec *codec, 329017314379SLydia Wang struct snd_pcm_substream *substream) 329117314379SLydia Wang { 329217314379SLydia Wang int idle = substream->pstr->substream_opened == 1 329317314379SLydia Wang && substream->ref_count == 0; 329417314379SLydia Wang 329517314379SLydia Wang analog_low_current_mode(codec, idle); 329617314379SLydia Wang return 0; 329717314379SLydia Wang } 329817314379SLydia Wang 3299f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { 33000aa62aefSHarald Welte .substreams = 2, 3301f7278fd0SJosepch Chan .channels_min = 2, 3302f7278fd0SJosepch Chan .channels_max = 8, 3303f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 3304f7278fd0SJosepch Chan .ops = { 3305f7278fd0SJosepch Chan .open = via_playback_pcm_open, 33060aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 330717314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 330817314379SLydia Wang .close = via_pcm_open_close 3309f7278fd0SJosepch Chan }, 3310f7278fd0SJosepch Chan }; 3311f7278fd0SJosepch Chan 3312f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { 33130aa62aefSHarald Welte .substreams = 2, 3314f7278fd0SJosepch Chan .channels_min = 2, 3315f7278fd0SJosepch Chan .channels_max = 4, 3316f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 3317f7278fd0SJosepch Chan .ops = { 3318f7278fd0SJosepch Chan .open = via_playback_pcm_open, 33190aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 33200aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 3321f7278fd0SJosepch Chan }, 3322f7278fd0SJosepch Chan }; 3323f7278fd0SJosepch Chan 3324f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_analog_capture = { 3325f7278fd0SJosepch Chan .substreams = 2, 3326f7278fd0SJosepch Chan .channels_min = 2, 3327f7278fd0SJosepch Chan .channels_max = 2, 3328f7278fd0SJosepch Chan .nid = 0x13, /* NID to query formats and rates */ 3329f7278fd0SJosepch Chan .ops = { 333017314379SLydia Wang .open = via_pcm_open_close, 3331f7278fd0SJosepch Chan .prepare = via_capture_pcm_prepare, 333217314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 333317314379SLydia Wang .close = via_pcm_open_close 3334f7278fd0SJosepch Chan }, 3335f7278fd0SJosepch Chan }; 3336f7278fd0SJosepch Chan 3337f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_playback = { 3338f7278fd0SJosepch Chan .substreams = 1, 3339f7278fd0SJosepch Chan .channels_min = 2, 3340f7278fd0SJosepch Chan .channels_max = 2, 3341f7278fd0SJosepch Chan /* NID is set in via_build_pcms */ 3342f7278fd0SJosepch Chan .ops = { 3343f7278fd0SJosepch Chan .open = via_dig_playback_pcm_open, 3344f7278fd0SJosepch Chan .close = via_dig_playback_pcm_close, 33459da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 33469da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3347f7278fd0SJosepch Chan }, 3348f7278fd0SJosepch Chan }; 3349f7278fd0SJosepch Chan 3350f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_capture = { 3351f7278fd0SJosepch Chan .substreams = 1, 3352f7278fd0SJosepch Chan .channels_min = 2, 3353f7278fd0SJosepch Chan .channels_max = 2, 3354f7278fd0SJosepch Chan }; 3355f7278fd0SJosepch Chan 3356f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */ 3357f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, 3358f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3359f7278fd0SJosepch Chan { 3360f7278fd0SJosepch Chan int i; 3361f7278fd0SJosepch Chan hda_nid_t nid; 3362f7278fd0SJosepch Chan 3363f7278fd0SJosepch Chan spec->multiout.num_dacs = cfg->line_outs; 3364f7278fd0SJosepch Chan 3365f7278fd0SJosepch Chan spec->multiout.dac_nids = spec->private_dac_nids; 3366f7278fd0SJosepch Chan 3367f7278fd0SJosepch Chan for (i = 0; i < 4; i++) { 3368f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 3369f7278fd0SJosepch Chan if (nid) { 3370f7278fd0SJosepch Chan /* config dac list */ 3371f7278fd0SJosepch Chan switch (i) { 3372f7278fd0SJosepch Chan case AUTO_SEQ_FRONT: 3373f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x10; 3374f7278fd0SJosepch Chan break; 3375f7278fd0SJosepch Chan case AUTO_SEQ_CENLFE: 3376f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x24; 3377f7278fd0SJosepch Chan break; 3378f7278fd0SJosepch Chan case AUTO_SEQ_SURROUND: 3379fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 3380f7278fd0SJosepch Chan break; 3381f7278fd0SJosepch Chan case AUTO_SEQ_SIDE: 3382fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x25; 3383f7278fd0SJosepch Chan break; 3384f7278fd0SJosepch Chan } 3385f7278fd0SJosepch Chan } 3386f7278fd0SJosepch Chan } 3387f7278fd0SJosepch Chan 3388f7278fd0SJosepch Chan return 0; 3389f7278fd0SJosepch Chan } 3390f7278fd0SJosepch Chan 3391f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */ 3392f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, 3393f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3394f7278fd0SJosepch Chan { 3395f7278fd0SJosepch Chan char name[32]; 3396f7278fd0SJosepch Chan static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 3397fb4cb772SHarald Welte hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27}; 3398f7278fd0SJosepch Chan hda_nid_t nid, nid_vol = 0; 3399f7278fd0SJosepch Chan int i, err; 3400f7278fd0SJosepch Chan 3401f7278fd0SJosepch Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3402f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 3403f7278fd0SJosepch Chan 3404f7278fd0SJosepch Chan if (!nid) 3405f7278fd0SJosepch Chan continue; 3406f7278fd0SJosepch Chan 3407f7278fd0SJosepch Chan nid_vol = nid_vols[i]; 3408f7278fd0SJosepch Chan 3409f7278fd0SJosepch Chan if (i == AUTO_SEQ_CENLFE) { 3410f7278fd0SJosepch Chan /* Center/LFE */ 3411f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3412f7278fd0SJosepch Chan "Center Playback Volume", 3413f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3414f7278fd0SJosepch Chan HDA_OUTPUT)); 3415f7278fd0SJosepch Chan if (err < 0) 3416f7278fd0SJosepch Chan return err; 3417f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3418f7278fd0SJosepch Chan "LFE Playback Volume", 3419f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3420f7278fd0SJosepch Chan HDA_OUTPUT)); 3421f7278fd0SJosepch Chan if (err < 0) 3422f7278fd0SJosepch Chan return err; 3423f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3424f7278fd0SJosepch Chan "Center Playback Switch", 3425f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3426f7278fd0SJosepch Chan HDA_OUTPUT)); 3427f7278fd0SJosepch Chan if (err < 0) 3428f7278fd0SJosepch Chan return err; 3429f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3430f7278fd0SJosepch Chan "LFE Playback Switch", 3431f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3432f7278fd0SJosepch Chan HDA_OUTPUT)); 3433f7278fd0SJosepch Chan if (err < 0) 3434f7278fd0SJosepch Chan return err; 3435f7278fd0SJosepch Chan } else if (i == AUTO_SEQ_FRONT) { 3436f7278fd0SJosepch Chan /* add control to mixer index 0 */ 3437f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3438f7278fd0SJosepch Chan "Master Front Playback Volume", 3439f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3440f7278fd0SJosepch Chan HDA_INPUT)); 3441f7278fd0SJosepch Chan if (err < 0) 3442f7278fd0SJosepch Chan return err; 3443f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3444f7278fd0SJosepch Chan "Master Front Playback Switch", 3445f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3446f7278fd0SJosepch Chan HDA_INPUT)); 3447f7278fd0SJosepch Chan if (err < 0) 3448f7278fd0SJosepch Chan return err; 3449f7278fd0SJosepch Chan 3450f7278fd0SJosepch Chan /* add control to PW3 */ 3451f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3452f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3453f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3454f7278fd0SJosepch Chan HDA_OUTPUT)); 3455f7278fd0SJosepch Chan if (err < 0) 3456f7278fd0SJosepch Chan return err; 3457f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3458f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3459f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3460f7278fd0SJosepch Chan HDA_OUTPUT)); 3461f7278fd0SJosepch Chan if (err < 0) 3462f7278fd0SJosepch Chan return err; 3463f7278fd0SJosepch Chan } else { 3464f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3465f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3466f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3467f7278fd0SJosepch Chan HDA_OUTPUT)); 3468f7278fd0SJosepch Chan if (err < 0) 3469f7278fd0SJosepch Chan return err; 3470f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3471f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3472f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3473f7278fd0SJosepch Chan HDA_OUTPUT)); 3474f7278fd0SJosepch Chan if (err < 0) 3475f7278fd0SJosepch Chan return err; 3476f7278fd0SJosepch Chan } 3477f7278fd0SJosepch Chan } 3478f7278fd0SJosepch Chan 3479f7278fd0SJosepch Chan return 0; 3480f7278fd0SJosepch Chan } 3481f7278fd0SJosepch Chan 3482f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3483f7278fd0SJosepch Chan { 3484f7278fd0SJosepch Chan int err; 3485f7278fd0SJosepch Chan 3486f7278fd0SJosepch Chan if (!pin) 3487f7278fd0SJosepch Chan return 0; 3488f7278fd0SJosepch Chan 3489f7278fd0SJosepch Chan spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ 3490cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3491f7278fd0SJosepch Chan 3492f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3493f7278fd0SJosepch Chan "Headphone Playback Volume", 3494f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3495f7278fd0SJosepch Chan if (err < 0) 3496f7278fd0SJosepch Chan return err; 3497f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3498f7278fd0SJosepch Chan "Headphone Playback Switch", 3499f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3500f7278fd0SJosepch Chan if (err < 0) 3501f7278fd0SJosepch Chan return err; 3502f7278fd0SJosepch Chan 35030aa62aefSHarald Welte create_hp_imux(spec); 35040aa62aefSHarald Welte 3505f7278fd0SJosepch Chan return 0; 3506f7278fd0SJosepch Chan } 3507f7278fd0SJosepch Chan 3508f7278fd0SJosepch Chan /* create playback/capture controls for input pins */ 3509f7278fd0SJosepch Chan static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec, 3510f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3511f7278fd0SJosepch Chan { 3512f7278fd0SJosepch Chan static char *labels[] = { 3513f7278fd0SJosepch Chan "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 3514f7278fd0SJosepch Chan }; 35150aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 3516f7278fd0SJosepch Chan int i, err, idx = 0; 3517f7278fd0SJosepch Chan 3518f7278fd0SJosepch Chan /* for internal loopback recording select */ 3519f7278fd0SJosepch Chan imux->items[imux->num_items].label = "Stereo Mixer"; 3520f7278fd0SJosepch Chan imux->items[imux->num_items].index = idx; 3521f7278fd0SJosepch Chan imux->num_items++; 3522f7278fd0SJosepch Chan 3523f7278fd0SJosepch Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 3524f7278fd0SJosepch Chan if (!cfg->input_pins[i]) 3525f7278fd0SJosepch Chan continue; 3526f7278fd0SJosepch Chan 3527f7278fd0SJosepch Chan switch (cfg->input_pins[i]) { 3528f7278fd0SJosepch Chan case 0x1a: /* Mic */ 3529f7278fd0SJosepch Chan idx = 2; 3530f7278fd0SJosepch Chan break; 3531f7278fd0SJosepch Chan 3532f7278fd0SJosepch Chan case 0x1b: /* Line In */ 3533f7278fd0SJosepch Chan idx = 3; 3534f7278fd0SJosepch Chan break; 3535f7278fd0SJosepch Chan 3536f7278fd0SJosepch Chan case 0x1e: /* Front Mic */ 3537f7278fd0SJosepch Chan idx = 4; 3538f7278fd0SJosepch Chan break; 3539f7278fd0SJosepch Chan 3540f7278fd0SJosepch Chan case 0x1f: /* CD */ 3541f7278fd0SJosepch Chan idx = 1; 3542f7278fd0SJosepch Chan break; 3543f7278fd0SJosepch Chan } 35449510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x16); 3545f7278fd0SJosepch Chan if (err < 0) 3546f7278fd0SJosepch Chan return err; 3547f7278fd0SJosepch Chan imux->items[imux->num_items].label = labels[i]; 3548f7278fd0SJosepch Chan imux->items[imux->num_items].index = idx; 3549f7278fd0SJosepch Chan imux->num_items++; 3550f7278fd0SJosepch Chan } 3551f7278fd0SJosepch Chan return 0; 3552f7278fd0SJosepch Chan } 3553f7278fd0SJosepch Chan 3554f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec) 3555f7278fd0SJosepch Chan { 3556f7278fd0SJosepch Chan struct via_spec *spec = codec->spec; 3557f7278fd0SJosepch Chan int err; 3558f7278fd0SJosepch Chan 3559f7278fd0SJosepch Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3560f7278fd0SJosepch Chan if (err < 0) 3561f7278fd0SJosepch Chan return err; 3562f7278fd0SJosepch Chan err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); 3563f7278fd0SJosepch Chan if (err < 0) 3564f7278fd0SJosepch Chan return err; 3565f7278fd0SJosepch Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3566f7278fd0SJosepch Chan return 0; /* can't find valid BIOS pin config */ 3567f7278fd0SJosepch Chan 3568f7278fd0SJosepch Chan err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); 3569f7278fd0SJosepch Chan if (err < 0) 3570f7278fd0SJosepch Chan return err; 3571f7278fd0SJosepch Chan err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3572f7278fd0SJosepch Chan if (err < 0) 3573f7278fd0SJosepch Chan return err; 3574f7278fd0SJosepch Chan err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg); 3575f7278fd0SJosepch Chan if (err < 0) 3576f7278fd0SJosepch Chan return err; 3577f7278fd0SJosepch Chan 3578f7278fd0SJosepch Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3579f7278fd0SJosepch Chan 35800852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 3581f7278fd0SJosepch Chan spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; 358255d1d6c1STakashi Iwai spec->dig_in_pin = VT1708B_DIGIN_PIN; 3583f7278fd0SJosepch Chan if (spec->autocfg.dig_in_pin) 3584f7278fd0SJosepch Chan spec->dig_in_nid = VT1708B_DIGIN_NID; 3585f7278fd0SJosepch Chan 3586603c4019STakashi Iwai if (spec->kctls.list) 3587603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3588f7278fd0SJosepch Chan 35890aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 35900aa62aefSHarald Welte 3591f8fdd495SHarald Welte if (spec->hp_mux) 35920aa62aefSHarald Welte spec->mixers[spec->num_mixers++] = via_hp_mixer; 3593f7278fd0SJosepch Chan 35941564b287SLydia Wang spec->mixers[spec->num_mixers++] = via_smart51_mixer; 3595f7278fd0SJosepch Chan return 1; 3596f7278fd0SJosepch Chan } 3597f7278fd0SJosepch Chan 3598f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3599f7278fd0SJosepch Chan static struct hda_amp_list vt1708B_loopbacks[] = { 3600f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 3601f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 3602f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 3603f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 3604f7278fd0SJosepch Chan { } /* end */ 3605f7278fd0SJosepch Chan }; 3606f7278fd0SJosepch Chan #endif 3607518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 3608f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 3609f7278fd0SJosepch Chan { 3610f7278fd0SJosepch Chan struct via_spec *spec; 3611f7278fd0SJosepch Chan int err; 3612f7278fd0SJosepch Chan 3613518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 3614518bf3baSLydia Wang return patch_vt1708S(codec); 3615f7278fd0SJosepch Chan /* create a codec specific record */ 3616eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 3617f7278fd0SJosepch Chan if (spec == NULL) 3618f7278fd0SJosepch Chan return -ENOMEM; 3619f7278fd0SJosepch Chan 3620f7278fd0SJosepch Chan codec->spec = spec; 3621f7278fd0SJosepch Chan 3622f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3623f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3624f7278fd0SJosepch Chan if (err < 0) { 3625f7278fd0SJosepch Chan via_free(codec); 3626f7278fd0SJosepch Chan return err; 3627f7278fd0SJosepch Chan } else if (!err) { 3628f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3629f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3630f7278fd0SJosepch Chan } 3631f7278fd0SJosepch Chan 363269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; 363369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3634f7278fd0SJosepch Chan 3635f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 3636f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; 3637f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3638f7278fd0SJosepch Chan 3639f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 3640f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3641f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3642f7278fd0SJosepch Chan 3643f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 3644f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 3645f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 3646337b9d02STakashi Iwai get_mux_nids(codec); 3647f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3648f7278fd0SJosepch Chan spec->num_mixers++; 3649f7278fd0SJosepch Chan } 3650f7278fd0SJosepch Chan 3651f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3652f7278fd0SJosepch Chan 3653f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 365469e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3655f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3656f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3657f7278fd0SJosepch Chan #endif 3658f7278fd0SJosepch Chan 3659f7278fd0SJosepch Chan return 0; 3660f7278fd0SJosepch Chan } 3661f7278fd0SJosepch Chan 3662f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 3663f7278fd0SJosepch Chan { 3664f7278fd0SJosepch Chan struct via_spec *spec; 3665f7278fd0SJosepch Chan int err; 3666f7278fd0SJosepch Chan 3667f7278fd0SJosepch Chan /* create a codec specific record */ 3668eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 3669f7278fd0SJosepch Chan if (spec == NULL) 3670f7278fd0SJosepch Chan return -ENOMEM; 3671f7278fd0SJosepch Chan 3672f7278fd0SJosepch Chan codec->spec = spec; 3673f7278fd0SJosepch Chan 3674f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3675f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3676f7278fd0SJosepch Chan if (err < 0) { 3677f7278fd0SJosepch Chan via_free(codec); 3678f7278fd0SJosepch Chan return err; 3679f7278fd0SJosepch Chan } else if (!err) { 3680f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3681f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3682f7278fd0SJosepch Chan } 3683f7278fd0SJosepch Chan 368469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; 368569e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3686f7278fd0SJosepch Chan 3687f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 3688f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; 3689f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3690f7278fd0SJosepch Chan 3691f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 3692f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3693f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3694f7278fd0SJosepch Chan 3695f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 3696f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 3697f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 3698337b9d02STakashi Iwai get_mux_nids(codec); 3699f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3700f7278fd0SJosepch Chan spec->num_mixers++; 3701f7278fd0SJosepch Chan } 3702f7278fd0SJosepch Chan 3703f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3704f7278fd0SJosepch Chan 3705f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 370669e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3707f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3708f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3709f7278fd0SJosepch Chan #endif 3710c577b8a1SJoseph Chan 3711c577b8a1SJoseph Chan return 0; 3712c577b8a1SJoseph Chan } 3713c577b8a1SJoseph Chan 3714d949cac1SHarald Welte /* Patch for VT1708S */ 3715d949cac1SHarald Welte 3716d949cac1SHarald Welte /* capture mixer elements */ 3717d949cac1SHarald Welte static struct snd_kcontrol_new vt1708S_capture_mixer[] = { 3718d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3719d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3720d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3721d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 37226369bcfcSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 37236369bcfcSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 37246369bcfcSLydia Wang HDA_INPUT), 3725d949cac1SHarald Welte { 3726d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3727d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3728d949cac1SHarald Welte * So call somewhat different.. 3729d949cac1SHarald Welte */ 3730d949cac1SHarald Welte /* .name = "Capture Source", */ 3731d949cac1SHarald Welte .name = "Input Source", 3732d949cac1SHarald Welte .count = 1, 3733d949cac1SHarald Welte .info = via_mux_enum_info, 3734d949cac1SHarald Welte .get = via_mux_enum_get, 3735d949cac1SHarald Welte .put = via_mux_enum_put, 3736d949cac1SHarald Welte }, 3737d949cac1SHarald Welte { } /* end */ 3738d949cac1SHarald Welte }; 3739d949cac1SHarald Welte 3740d949cac1SHarald Welte static struct hda_verb vt1708S_volume_init_verbs[] = { 3741d949cac1SHarald Welte /* Unmute ADC0-1 and set the default input to mic-in */ 3742d949cac1SHarald Welte {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3743d949cac1SHarald Welte {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3744d949cac1SHarald Welte 3745d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the 3746d949cac1SHarald Welte * analog-loopback mixer widget */ 3747d949cac1SHarald Welte /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3748d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3749d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3750d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3751d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3752d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3753d949cac1SHarald Welte 3754d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3755d949cac1SHarald Welte {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 37565691ec7fSHarald Welte /* PW9, PW10 Output enable */ 3757d949cac1SHarald Welte {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 37585691ec7fSHarald Welte {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3759d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 3760d7426329SHarald Welte {0x1, 0xf98, 0x1}, 3761bc7e7e5cSLydia Wang /* don't bybass mixer */ 3762bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 3763d949cac1SHarald Welte { } 3764d949cac1SHarald Welte }; 3765d949cac1SHarald Welte 376669e52a80SHarald Welte static struct hda_verb vt1708S_uniwill_init_verbs[] = { 3767a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3768a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3769a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3770a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3771a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3772a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3773a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3774a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3775a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 377669e52a80SHarald Welte { } 377769e52a80SHarald Welte }; 377869e52a80SHarald Welte 3779d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_playback = { 3780d949cac1SHarald Welte .substreams = 2, 3781d949cac1SHarald Welte .channels_min = 2, 3782d949cac1SHarald Welte .channels_max = 8, 3783d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 3784d949cac1SHarald Welte .ops = { 3785d949cac1SHarald Welte .open = via_playback_pcm_open, 3786c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 3787c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 378817314379SLydia Wang .close = via_pcm_open_close 3789d949cac1SHarald Welte }, 3790d949cac1SHarald Welte }; 3791d949cac1SHarald Welte 3792d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_capture = { 3793d949cac1SHarald Welte .substreams = 2, 3794d949cac1SHarald Welte .channels_min = 2, 3795d949cac1SHarald Welte .channels_max = 2, 3796d949cac1SHarald Welte .nid = 0x13, /* NID to query formats and rates */ 3797d949cac1SHarald Welte .ops = { 379817314379SLydia Wang .open = via_pcm_open_close, 3799d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 380017314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 380117314379SLydia Wang .close = via_pcm_open_close 3802d949cac1SHarald Welte }, 3803d949cac1SHarald Welte }; 3804d949cac1SHarald Welte 3805d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_digital_playback = { 38069da29271STakashi Iwai .substreams = 1, 3807d949cac1SHarald Welte .channels_min = 2, 3808d949cac1SHarald Welte .channels_max = 2, 3809d949cac1SHarald Welte /* NID is set in via_build_pcms */ 3810d949cac1SHarald Welte .ops = { 3811d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 3812d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 38139da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 38149da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3815d949cac1SHarald Welte }, 3816d949cac1SHarald Welte }; 3817d949cac1SHarald Welte 3818d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 3819d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec, 3820d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3821d949cac1SHarald Welte { 3822d949cac1SHarald Welte int i; 3823d949cac1SHarald Welte hda_nid_t nid; 3824d949cac1SHarald Welte 3825d949cac1SHarald Welte spec->multiout.num_dacs = cfg->line_outs; 3826d949cac1SHarald Welte 3827d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 3828d949cac1SHarald Welte 3829d949cac1SHarald Welte for (i = 0; i < 4; i++) { 3830d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3831d949cac1SHarald Welte if (nid) { 3832d949cac1SHarald Welte /* config dac list */ 3833d949cac1SHarald Welte switch (i) { 3834d949cac1SHarald Welte case AUTO_SEQ_FRONT: 3835d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x10; 3836d949cac1SHarald Welte break; 3837d949cac1SHarald Welte case AUTO_SEQ_CENLFE: 3838d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x24; 3839d949cac1SHarald Welte break; 3840d949cac1SHarald Welte case AUTO_SEQ_SURROUND: 3841d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x11; 3842d949cac1SHarald Welte break; 3843d949cac1SHarald Welte case AUTO_SEQ_SIDE: 3844d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x25; 3845d949cac1SHarald Welte break; 3846d949cac1SHarald Welte } 3847d949cac1SHarald Welte } 3848d949cac1SHarald Welte } 3849d949cac1SHarald Welte 3850d949cac1SHarald Welte return 0; 3851d949cac1SHarald Welte } 3852d949cac1SHarald Welte 3853d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 3854d949cac1SHarald Welte static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec, 3855d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3856d949cac1SHarald Welte { 3857d949cac1SHarald Welte char name[32]; 3858d949cac1SHarald Welte static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 3859d949cac1SHarald Welte hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25}; 3860d949cac1SHarald Welte hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27}; 3861d949cac1SHarald Welte hda_nid_t nid, nid_vol, nid_mute; 3862d949cac1SHarald Welte int i, err; 3863d949cac1SHarald Welte 3864d949cac1SHarald Welte for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3865d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3866d949cac1SHarald Welte 3867d949cac1SHarald Welte if (!nid) 3868d949cac1SHarald Welte continue; 3869d949cac1SHarald Welte 3870d949cac1SHarald Welte nid_vol = nid_vols[i]; 3871d949cac1SHarald Welte nid_mute = nid_mutes[i]; 3872d949cac1SHarald Welte 3873d949cac1SHarald Welte if (i == AUTO_SEQ_CENLFE) { 3874d949cac1SHarald Welte /* Center/LFE */ 3875d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3876d949cac1SHarald Welte "Center Playback Volume", 3877d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3878d949cac1SHarald Welte HDA_OUTPUT)); 3879d949cac1SHarald Welte if (err < 0) 3880d949cac1SHarald Welte return err; 3881d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3882d949cac1SHarald Welte "LFE Playback Volume", 3883d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3884d949cac1SHarald Welte HDA_OUTPUT)); 3885d949cac1SHarald Welte if (err < 0) 3886d949cac1SHarald Welte return err; 3887d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3888d949cac1SHarald Welte "Center Playback Switch", 3889d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3890d949cac1SHarald Welte 1, 0, 3891d949cac1SHarald Welte HDA_OUTPUT)); 3892d949cac1SHarald Welte if (err < 0) 3893d949cac1SHarald Welte return err; 3894d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3895d949cac1SHarald Welte "LFE Playback Switch", 3896d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3897d949cac1SHarald Welte 2, 0, 3898d949cac1SHarald Welte HDA_OUTPUT)); 3899d949cac1SHarald Welte if (err < 0) 3900d949cac1SHarald Welte return err; 3901d949cac1SHarald Welte } else if (i == AUTO_SEQ_FRONT) { 3902d949cac1SHarald Welte /* add control to mixer index 0 */ 3903d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3904d949cac1SHarald Welte "Master Front Playback Volume", 3905d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3906d949cac1SHarald Welte HDA_INPUT)); 3907d949cac1SHarald Welte if (err < 0) 3908d949cac1SHarald Welte return err; 3909d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3910d949cac1SHarald Welte "Master Front Playback Switch", 3911d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3912d949cac1SHarald Welte HDA_INPUT)); 3913d949cac1SHarald Welte if (err < 0) 3914d949cac1SHarald Welte return err; 3915d949cac1SHarald Welte 3916d949cac1SHarald Welte /* Front */ 3917d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3918d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3919d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3920d949cac1SHarald Welte HDA_OUTPUT)); 3921d949cac1SHarald Welte if (err < 0) 3922d949cac1SHarald Welte return err; 3923d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 3924d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3925d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3926d949cac1SHarald Welte 3, 0, 3927d949cac1SHarald Welte HDA_OUTPUT)); 3928d949cac1SHarald Welte if (err < 0) 3929d949cac1SHarald Welte return err; 3930d949cac1SHarald Welte } else { 3931d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3932d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3933d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3934d949cac1SHarald Welte HDA_OUTPUT)); 3935d949cac1SHarald Welte if (err < 0) 3936d949cac1SHarald Welte return err; 3937d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 3938d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3939d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3940d949cac1SHarald Welte 3, 0, 3941d949cac1SHarald Welte HDA_OUTPUT)); 3942d949cac1SHarald Welte if (err < 0) 3943d949cac1SHarald Welte return err; 3944d949cac1SHarald Welte } 3945d949cac1SHarald Welte } 3946d949cac1SHarald Welte 3947d949cac1SHarald Welte return 0; 3948d949cac1SHarald Welte } 3949d949cac1SHarald Welte 3950d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3951d949cac1SHarald Welte { 3952d949cac1SHarald Welte int err; 3953d949cac1SHarald Welte 3954d949cac1SHarald Welte if (!pin) 3955d949cac1SHarald Welte return 0; 3956d949cac1SHarald Welte 3957d949cac1SHarald Welte spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ 3958cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3959d949cac1SHarald Welte 3960d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3961d949cac1SHarald Welte "Headphone Playback Volume", 3962d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 3963d949cac1SHarald Welte if (err < 0) 3964d949cac1SHarald Welte return err; 3965d949cac1SHarald Welte 3966d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3967d949cac1SHarald Welte "Headphone Playback Switch", 3968d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3969d949cac1SHarald Welte if (err < 0) 3970d949cac1SHarald Welte return err; 3971d949cac1SHarald Welte 39720aa62aefSHarald Welte create_hp_imux(spec); 39730aa62aefSHarald Welte 3974d949cac1SHarald Welte return 0; 3975d949cac1SHarald Welte } 3976d949cac1SHarald Welte 3977d949cac1SHarald Welte /* create playback/capture controls for input pins */ 3978d949cac1SHarald Welte static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec, 3979d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3980d949cac1SHarald Welte { 3981d949cac1SHarald Welte static char *labels[] = { 3982d949cac1SHarald Welte "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 3983d949cac1SHarald Welte }; 39840aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 3985d949cac1SHarald Welte int i, err, idx = 0; 3986d949cac1SHarald Welte 3987d949cac1SHarald Welte /* for internal loopback recording select */ 3988d949cac1SHarald Welte imux->items[imux->num_items].label = "Stereo Mixer"; 3989d949cac1SHarald Welte imux->items[imux->num_items].index = 5; 3990d949cac1SHarald Welte imux->num_items++; 3991d949cac1SHarald Welte 3992d949cac1SHarald Welte for (i = 0; i < AUTO_PIN_LAST; i++) { 3993d949cac1SHarald Welte if (!cfg->input_pins[i]) 3994d949cac1SHarald Welte continue; 3995d949cac1SHarald Welte 3996d949cac1SHarald Welte switch (cfg->input_pins[i]) { 3997d949cac1SHarald Welte case 0x1a: /* Mic */ 3998d949cac1SHarald Welte idx = 2; 3999d949cac1SHarald Welte break; 4000d949cac1SHarald Welte 4001d949cac1SHarald Welte case 0x1b: /* Line In */ 4002d949cac1SHarald Welte idx = 3; 4003d949cac1SHarald Welte break; 4004d949cac1SHarald Welte 4005d949cac1SHarald Welte case 0x1e: /* Front Mic */ 4006d949cac1SHarald Welte idx = 4; 4007d949cac1SHarald Welte break; 4008d949cac1SHarald Welte 4009d949cac1SHarald Welte case 0x1f: /* CD */ 4010d949cac1SHarald Welte idx = 1; 4011d949cac1SHarald Welte break; 4012d949cac1SHarald Welte } 40139510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x16); 4014d949cac1SHarald Welte if (err < 0) 4015d949cac1SHarald Welte return err; 4016d949cac1SHarald Welte imux->items[imux->num_items].label = labels[i]; 4017d949cac1SHarald Welte imux->items[imux->num_items].index = idx-1; 4018d949cac1SHarald Welte imux->num_items++; 4019d949cac1SHarald Welte } 4020d949cac1SHarald Welte return 0; 4021d949cac1SHarald Welte } 4022d949cac1SHarald Welte 40239da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 40249da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 40259da29271STakashi Iwai { 40269da29271STakashi Iwai struct via_spec *spec = codec->spec; 40279da29271STakashi Iwai int i; 40289da29271STakashi Iwai 40299da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 40309da29271STakashi Iwai hda_nid_t nid; 40319da29271STakashi Iwai int conn; 40329da29271STakashi Iwai 40339da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 40349da29271STakashi Iwai if (!nid) 40359da29271STakashi Iwai continue; 40369da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 40379da29271STakashi Iwai if (conn < 1) 40389da29271STakashi Iwai continue; 40399da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 40409da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 40419da29271STakashi Iwai else { 40429da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 40439da29271STakashi Iwai break; /* at most two dig outs */ 40449da29271STakashi Iwai } 40459da29271STakashi Iwai } 40469da29271STakashi Iwai } 40479da29271STakashi Iwai 4048d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec) 4049d949cac1SHarald Welte { 4050d949cac1SHarald Welte struct via_spec *spec = codec->spec; 4051d949cac1SHarald Welte int err; 4052d949cac1SHarald Welte 40539da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4054d949cac1SHarald Welte if (err < 0) 4055d949cac1SHarald Welte return err; 4056d949cac1SHarald Welte err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); 4057d949cac1SHarald Welte if (err < 0) 4058d949cac1SHarald Welte return err; 4059d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4060d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 4061d949cac1SHarald Welte 4062d949cac1SHarald Welte err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg); 4063d949cac1SHarald Welte if (err < 0) 4064d949cac1SHarald Welte return err; 4065d949cac1SHarald Welte err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4066d949cac1SHarald Welte if (err < 0) 4067d949cac1SHarald Welte return err; 4068d949cac1SHarald Welte err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg); 4069d949cac1SHarald Welte if (err < 0) 4070d949cac1SHarald Welte return err; 4071d949cac1SHarald Welte 4072d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4073d949cac1SHarald Welte 40749da29271STakashi Iwai fill_dig_outs(codec); 407598aa34c0SHarald Welte 4076603c4019STakashi Iwai if (spec->kctls.list) 4077603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 4078d949cac1SHarald Welte 40790aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 40800aa62aefSHarald Welte 4081f8fdd495SHarald Welte if (spec->hp_mux) 40820aa62aefSHarald Welte spec->mixers[spec->num_mixers++] = via_hp_mixer; 4083d949cac1SHarald Welte 40841564b287SLydia Wang spec->mixers[spec->num_mixers++] = via_smart51_mixer; 4085d949cac1SHarald Welte return 1; 4086d949cac1SHarald Welte } 4087d949cac1SHarald Welte 4088d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4089d949cac1SHarald Welte static struct hda_amp_list vt1708S_loopbacks[] = { 4090d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 4091d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 4092d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 4093d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 4094d949cac1SHarald Welte { } /* end */ 4095d949cac1SHarald Welte }; 4096d949cac1SHarald Welte #endif 4097d949cac1SHarald Welte 40986369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 40996369bcfcSLydia Wang int offset, int num_steps, int step_size) 41006369bcfcSLydia Wang { 41016369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 41026369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 41036369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 41046369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 41056369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 41066369bcfcSLydia Wang } 41076369bcfcSLydia Wang 4108d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 4109d949cac1SHarald Welte { 4110d949cac1SHarald Welte struct via_spec *spec; 4111d949cac1SHarald Welte int err; 4112d949cac1SHarald Welte 4113d949cac1SHarald Welte /* create a codec specific record */ 4114d949cac1SHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 4115d949cac1SHarald Welte if (spec == NULL) 4116d949cac1SHarald Welte return -ENOMEM; 4117d949cac1SHarald Welte 4118d949cac1SHarald Welte codec->spec = spec; 4119d949cac1SHarald Welte 4120d949cac1SHarald Welte /* automatic parse from the BIOS config */ 4121d949cac1SHarald Welte err = vt1708S_parse_auto_config(codec); 4122d949cac1SHarald Welte if (err < 0) { 4123d949cac1SHarald Welte via_free(codec); 4124d949cac1SHarald Welte return err; 4125d949cac1SHarald Welte } else if (!err) { 4126d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 4127d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 4128d949cac1SHarald Welte } 4129d949cac1SHarald Welte 413069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; 413169e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs; 4132d949cac1SHarald Welte 4133d949cac1SHarald Welte spec->stream_name_analog = "VT1708S Analog"; 4134d949cac1SHarald Welte spec->stream_analog_playback = &vt1708S_pcm_analog_playback; 4135d949cac1SHarald Welte spec->stream_analog_capture = &vt1708S_pcm_analog_capture; 4136d949cac1SHarald Welte 4137d949cac1SHarald Welte spec->stream_name_digital = "VT1708S Digital"; 4138d949cac1SHarald Welte spec->stream_digital_playback = &vt1708S_pcm_digital_playback; 4139d949cac1SHarald Welte 4140d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 4141d949cac1SHarald Welte spec->adc_nids = vt1708S_adc_nids; 4142d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); 4143337b9d02STakashi Iwai get_mux_nids(codec); 41446369bcfcSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 41456369bcfcSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 4146d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; 4147d949cac1SHarald Welte spec->num_mixers++; 4148d949cac1SHarald Welte } 4149d949cac1SHarald Welte 4150d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 4151d949cac1SHarald Welte 4152d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 415369e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 4154d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4155d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 4156d949cac1SHarald Welte #endif 4157d949cac1SHarald Welte 4158518bf3baSLydia Wang /* correct names for VT1708BCE */ 4159518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 4160518bf3baSLydia Wang kfree(codec->chip_name); 4161518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 4162518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 4163518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 4164518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 4165518bf3baSLydia Wang spec->stream_name_analog = "VT1708BCE Analog"; 4166518bf3baSLydia Wang spec->stream_name_digital = "VT1708BCE Digital"; 4167518bf3baSLydia Wang } 4168d949cac1SHarald Welte return 0; 4169d949cac1SHarald Welte } 4170d949cac1SHarald Welte 4171d949cac1SHarald Welte /* Patch for VT1702 */ 4172d949cac1SHarald Welte 4173d949cac1SHarald Welte /* capture mixer elements */ 4174d949cac1SHarald Welte static struct snd_kcontrol_new vt1702_capture_mixer[] = { 4175d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), 4176d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), 4177d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), 4178d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), 4179d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), 4180d949cac1SHarald Welte HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), 4181d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, 4182d949cac1SHarald Welte HDA_INPUT), 4183d949cac1SHarald Welte { 4184d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4185d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 4186d949cac1SHarald Welte * So call somewhat different.. 4187d949cac1SHarald Welte */ 4188d949cac1SHarald Welte /* .name = "Capture Source", */ 4189d949cac1SHarald Welte .name = "Input Source", 4190d949cac1SHarald Welte .count = 1, 4191d949cac1SHarald Welte .info = via_mux_enum_info, 4192d949cac1SHarald Welte .get = via_mux_enum_get, 4193d949cac1SHarald Welte .put = via_mux_enum_put, 4194d949cac1SHarald Welte }, 4195d949cac1SHarald Welte { } /* end */ 4196d949cac1SHarald Welte }; 4197d949cac1SHarald Welte 4198d949cac1SHarald Welte static struct hda_verb vt1702_volume_init_verbs[] = { 4199d949cac1SHarald Welte /* 4200d949cac1SHarald Welte * Unmute ADC0-1 and set the default input to mic-in 4201d949cac1SHarald Welte */ 4202d949cac1SHarald Welte {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4203d949cac1SHarald Welte {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4204d949cac1SHarald Welte {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4205d949cac1SHarald Welte 4206d949cac1SHarald Welte 4207d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4208d949cac1SHarald Welte * mixer widget 4209d949cac1SHarald Welte */ 4210d949cac1SHarald Welte /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ 4211d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4212d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4213d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 4214d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 4215d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4216d949cac1SHarald Welte 4217d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 4218d949cac1SHarald Welte {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, 4219d949cac1SHarald Welte /* PW6 PW7 Output enable */ 4220d949cac1SHarald Welte {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4221d949cac1SHarald Welte {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4222bc7e7e5cSLydia Wang /* mixer enable */ 4223bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 4224bc7e7e5cSLydia Wang /* GPIO 0~2 */ 4225bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 4226d949cac1SHarald Welte { } 4227d949cac1SHarald Welte }; 4228d949cac1SHarald Welte 422969e52a80SHarald Welte static struct hda_verb vt1702_uniwill_init_verbs[] = { 4230a34df19aSLydia Wang {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, 4231a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4232a34df19aSLydia Wang {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4233a34df19aSLydia Wang {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4234a34df19aSLydia Wang {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4235a34df19aSLydia Wang {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 423669e52a80SHarald Welte { } 423769e52a80SHarald Welte }; 423869e52a80SHarald Welte 4239d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_playback = { 42400aa62aefSHarald Welte .substreams = 2, 4241d949cac1SHarald Welte .channels_min = 2, 4242d949cac1SHarald Welte .channels_max = 2, 4243d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 4244d949cac1SHarald Welte .ops = { 4245d949cac1SHarald Welte .open = via_playback_pcm_open, 42460aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 424717314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 424817314379SLydia Wang .close = via_pcm_open_close 4249d949cac1SHarald Welte }, 4250d949cac1SHarald Welte }; 4251d949cac1SHarald Welte 4252d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_capture = { 4253d949cac1SHarald Welte .substreams = 3, 4254d949cac1SHarald Welte .channels_min = 2, 4255d949cac1SHarald Welte .channels_max = 2, 4256d949cac1SHarald Welte .nid = 0x12, /* NID to query formats and rates */ 4257d949cac1SHarald Welte .ops = { 425817314379SLydia Wang .open = via_pcm_open_close, 4259d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 426017314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 426117314379SLydia Wang .close = via_pcm_open_close 4262d949cac1SHarald Welte }, 4263d949cac1SHarald Welte }; 4264d949cac1SHarald Welte 4265d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_digital_playback = { 42665691ec7fSHarald Welte .substreams = 2, 4267d949cac1SHarald Welte .channels_min = 2, 4268d949cac1SHarald Welte .channels_max = 2, 4269d949cac1SHarald Welte /* NID is set in via_build_pcms */ 4270d949cac1SHarald Welte .ops = { 4271d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 4272d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 42739da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 42749da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 4275d949cac1SHarald Welte }, 4276d949cac1SHarald Welte }; 4277d949cac1SHarald Welte 4278d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 4279d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec, 4280d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4281d949cac1SHarald Welte { 4282d949cac1SHarald Welte spec->multiout.num_dacs = 1; 4283d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 4284d949cac1SHarald Welte 4285d949cac1SHarald Welte if (cfg->line_out_pins[0]) { 4286d949cac1SHarald Welte /* config dac list */ 4287d949cac1SHarald Welte spec->multiout.dac_nids[0] = 0x10; 4288d949cac1SHarald Welte } 4289d949cac1SHarald Welte 4290d949cac1SHarald Welte return 0; 4291d949cac1SHarald Welte } 4292d949cac1SHarald Welte 4293d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 4294d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, 4295d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4296d949cac1SHarald Welte { 4297d949cac1SHarald Welte int err; 4298d949cac1SHarald Welte 4299d949cac1SHarald Welte if (!cfg->line_out_pins[0]) 4300d949cac1SHarald Welte return -1; 4301d949cac1SHarald Welte 4302d949cac1SHarald Welte /* add control to mixer index 0 */ 4303d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4304d949cac1SHarald Welte "Master Front Playback Volume", 4305d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4306d949cac1SHarald Welte if (err < 0) 4307d949cac1SHarald Welte return err; 4308d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4309d949cac1SHarald Welte "Master Front Playback Switch", 4310d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4311d949cac1SHarald Welte if (err < 0) 4312d949cac1SHarald Welte return err; 4313d949cac1SHarald Welte 4314d949cac1SHarald Welte /* Front */ 4315d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4316d949cac1SHarald Welte "Front Playback Volume", 4317d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT)); 4318d949cac1SHarald Welte if (err < 0) 4319d949cac1SHarald Welte return err; 4320d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4321d949cac1SHarald Welte "Front Playback Switch", 4322d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT)); 4323d949cac1SHarald Welte if (err < 0) 4324d949cac1SHarald Welte return err; 4325d949cac1SHarald Welte 4326d949cac1SHarald Welte return 0; 4327d949cac1SHarald Welte } 4328d949cac1SHarald Welte 4329d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4330d949cac1SHarald Welte { 43310713efebSLydia Wang int err, i; 43320713efebSLydia Wang struct hda_input_mux *imux; 43330713efebSLydia Wang static const char *texts[] = { "ON", "OFF", NULL}; 4334d949cac1SHarald Welte if (!pin) 4335d949cac1SHarald Welte return 0; 4336d949cac1SHarald Welte spec->multiout.hp_nid = 0x1D; 4337cdc1784dSLydia Wang spec->hp_independent_mode_index = 0; 4338d949cac1SHarald Welte 4339d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4340d949cac1SHarald Welte "Headphone Playback Volume", 4341d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT)); 4342d949cac1SHarald Welte if (err < 0) 4343d949cac1SHarald Welte return err; 4344d949cac1SHarald Welte 4345d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4346d949cac1SHarald Welte "Headphone Playback Switch", 4347d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4348d949cac1SHarald Welte if (err < 0) 4349d949cac1SHarald Welte return err; 4350d949cac1SHarald Welte 43510713efebSLydia Wang imux = &spec->private_imux[1]; 43520aa62aefSHarald Welte 43530713efebSLydia Wang /* for hp mode select */ 43540713efebSLydia Wang i = 0; 43550713efebSLydia Wang while (texts[i] != NULL) { 43560713efebSLydia Wang imux->items[imux->num_items].label = texts[i]; 43570713efebSLydia Wang imux->items[imux->num_items].index = i; 43580713efebSLydia Wang imux->num_items++; 43590713efebSLydia Wang i++; 43600713efebSLydia Wang } 43610713efebSLydia Wang 43620713efebSLydia Wang spec->hp_mux = &spec->private_imux[1]; 4363d949cac1SHarald Welte return 0; 4364d949cac1SHarald Welte } 4365d949cac1SHarald Welte 4366d949cac1SHarald Welte /* create playback/capture controls for input pins */ 4367d949cac1SHarald Welte static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec, 4368d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4369d949cac1SHarald Welte { 4370d949cac1SHarald Welte static char *labels[] = { 4371d949cac1SHarald Welte "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 4372d949cac1SHarald Welte }; 43730aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 4374d949cac1SHarald Welte int i, err, idx = 0; 4375d949cac1SHarald Welte 4376d949cac1SHarald Welte /* for internal loopback recording select */ 4377d949cac1SHarald Welte imux->items[imux->num_items].label = "Stereo Mixer"; 4378d949cac1SHarald Welte imux->items[imux->num_items].index = 3; 4379d949cac1SHarald Welte imux->num_items++; 4380d949cac1SHarald Welte 4381d949cac1SHarald Welte for (i = 0; i < AUTO_PIN_LAST; i++) { 4382d949cac1SHarald Welte if (!cfg->input_pins[i]) 4383d949cac1SHarald Welte continue; 4384d949cac1SHarald Welte 4385d949cac1SHarald Welte switch (cfg->input_pins[i]) { 4386d949cac1SHarald Welte case 0x14: /* Mic */ 4387d949cac1SHarald Welte idx = 1; 4388d949cac1SHarald Welte break; 4389d949cac1SHarald Welte 4390d949cac1SHarald Welte case 0x15: /* Line In */ 4391d949cac1SHarald Welte idx = 2; 4392d949cac1SHarald Welte break; 4393d949cac1SHarald Welte 4394d949cac1SHarald Welte case 0x18: /* Front Mic */ 4395d949cac1SHarald Welte idx = 3; 4396d949cac1SHarald Welte break; 4397d949cac1SHarald Welte } 43989510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x1A); 4399d949cac1SHarald Welte if (err < 0) 4400d949cac1SHarald Welte return err; 4401d949cac1SHarald Welte imux->items[imux->num_items].label = labels[i]; 4402d949cac1SHarald Welte imux->items[imux->num_items].index = idx-1; 4403d949cac1SHarald Welte imux->num_items++; 4404d949cac1SHarald Welte } 4405d949cac1SHarald Welte return 0; 4406d949cac1SHarald Welte } 4407d949cac1SHarald Welte 4408d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec) 4409d949cac1SHarald Welte { 4410d949cac1SHarald Welte struct via_spec *spec = codec->spec; 4411d949cac1SHarald Welte int err; 4412d949cac1SHarald Welte 44139da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4414d949cac1SHarald Welte if (err < 0) 4415d949cac1SHarald Welte return err; 4416d949cac1SHarald Welte err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); 4417d949cac1SHarald Welte if (err < 0) 4418d949cac1SHarald Welte return err; 4419d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4420d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 4421d949cac1SHarald Welte 4422d949cac1SHarald Welte err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg); 4423d949cac1SHarald Welte if (err < 0) 4424d949cac1SHarald Welte return err; 4425d949cac1SHarald Welte err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4426d949cac1SHarald Welte if (err < 0) 4427d949cac1SHarald Welte return err; 4428c2c02ea3SLydia Wang /* limit AA path volume to 0 dB */ 4429c2c02ea3SLydia Wang snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 4430c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 4431c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 4432c2c02ea3SLydia Wang (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 4433c2c02ea3SLydia Wang (1 << AC_AMPCAP_MUTE_SHIFT)); 4434d949cac1SHarald Welte err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg); 4435d949cac1SHarald Welte if (err < 0) 4436d949cac1SHarald Welte return err; 4437d949cac1SHarald Welte 4438d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4439d949cac1SHarald Welte 44409da29271STakashi Iwai fill_dig_outs(codec); 444198aa34c0SHarald Welte 4442603c4019STakashi Iwai if (spec->kctls.list) 4443603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 4444d949cac1SHarald Welte 44450aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 44460aa62aefSHarald Welte 4447f8fdd495SHarald Welte if (spec->hp_mux) 44480aa62aefSHarald Welte spec->mixers[spec->num_mixers++] = via_hp_mixer; 4449d949cac1SHarald Welte 4450d949cac1SHarald Welte return 1; 4451d949cac1SHarald Welte } 4452d949cac1SHarald Welte 4453d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4454d949cac1SHarald Welte static struct hda_amp_list vt1702_loopbacks[] = { 4455d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 4456d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 4457d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 4458d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 4459d949cac1SHarald Welte { } /* end */ 4460d949cac1SHarald Welte }; 4461d949cac1SHarald Welte #endif 4462d949cac1SHarald Welte 4463d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 4464d949cac1SHarald Welte { 4465d949cac1SHarald Welte struct via_spec *spec; 4466d949cac1SHarald Welte int err; 4467d949cac1SHarald Welte 4468d949cac1SHarald Welte /* create a codec specific record */ 4469d949cac1SHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 4470d949cac1SHarald Welte if (spec == NULL) 4471d949cac1SHarald Welte return -ENOMEM; 4472d949cac1SHarald Welte 4473d949cac1SHarald Welte codec->spec = spec; 4474d949cac1SHarald Welte 4475d949cac1SHarald Welte /* automatic parse from the BIOS config */ 4476d949cac1SHarald Welte err = vt1702_parse_auto_config(codec); 4477d949cac1SHarald Welte if (err < 0) { 4478d949cac1SHarald Welte via_free(codec); 4479d949cac1SHarald Welte return err; 4480d949cac1SHarald Welte } else if (!err) { 4481d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 4482d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 4483d949cac1SHarald Welte } 4484d949cac1SHarald Welte 448569e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; 448669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; 4487d949cac1SHarald Welte 4488d949cac1SHarald Welte spec->stream_name_analog = "VT1702 Analog"; 4489d949cac1SHarald Welte spec->stream_analog_playback = &vt1702_pcm_analog_playback; 4490d949cac1SHarald Welte spec->stream_analog_capture = &vt1702_pcm_analog_capture; 4491d949cac1SHarald Welte 4492d949cac1SHarald Welte spec->stream_name_digital = "VT1702 Digital"; 4493d949cac1SHarald Welte spec->stream_digital_playback = &vt1702_pcm_digital_playback; 4494d949cac1SHarald Welte 4495d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 4496d949cac1SHarald Welte spec->adc_nids = vt1702_adc_nids; 4497d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids); 4498337b9d02STakashi Iwai get_mux_nids(codec); 4499d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1702_capture_mixer; 4500d949cac1SHarald Welte spec->num_mixers++; 4501d949cac1SHarald Welte } 4502d949cac1SHarald Welte 4503d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 4504d949cac1SHarald Welte 4505d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 450669e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 4507d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4508d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 4509d949cac1SHarald Welte #endif 4510d949cac1SHarald Welte 4511d949cac1SHarald Welte return 0; 4512d949cac1SHarald Welte } 4513d949cac1SHarald Welte 4514eb7188caSLydia Wang /* Patch for VT1718S */ 4515eb7188caSLydia Wang 4516eb7188caSLydia Wang /* capture mixer elements */ 4517eb7188caSLydia Wang static struct snd_kcontrol_new vt1718S_capture_mixer[] = { 4518eb7188caSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 4519eb7188caSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 4520eb7188caSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 4521eb7188caSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 4522eb7188caSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 4523eb7188caSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 4524eb7188caSLydia Wang HDA_INPUT), 4525eb7188caSLydia Wang { 4526eb7188caSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4527eb7188caSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 4528eb7188caSLydia Wang * So call somewhat different.. 4529eb7188caSLydia Wang */ 4530eb7188caSLydia Wang .name = "Input Source", 4531eb7188caSLydia Wang .count = 2, 4532eb7188caSLydia Wang .info = via_mux_enum_info, 4533eb7188caSLydia Wang .get = via_mux_enum_get, 4534eb7188caSLydia Wang .put = via_mux_enum_put, 4535eb7188caSLydia Wang }, 4536eb7188caSLydia Wang { } /* end */ 4537eb7188caSLydia Wang }; 4538eb7188caSLydia Wang 4539eb7188caSLydia Wang static struct hda_verb vt1718S_volume_init_verbs[] = { 4540eb7188caSLydia Wang /* 4541eb7188caSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4542eb7188caSLydia Wang */ 4543eb7188caSLydia Wang {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4544eb7188caSLydia Wang {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4545eb7188caSLydia Wang 4546eb7188caSLydia Wang 4547eb7188caSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4548eb7188caSLydia Wang * mixer widget 4549eb7188caSLydia Wang */ 4550eb7188caSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4551eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4552eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4553eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4554eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4555eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, 4556eb7188caSLydia Wang 4557eb7188caSLydia Wang /* Setup default input of Front HP to MW9 */ 4558eb7188caSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 4559eb7188caSLydia Wang /* PW9 PW10 Output enable */ 4560eb7188caSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4561eb7188caSLydia Wang {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4562eb7188caSLydia Wang /* PW11 Input enable */ 4563eb7188caSLydia Wang {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, 4564eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 4565eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 4566eb7188caSLydia Wang /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ 4567eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4568eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4569eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4570eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4571eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4572eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4573eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4574eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4575eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4576eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4577eb7188caSLydia Wang /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ 4578eb7188caSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, 4579eb7188caSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, 4580eb7188caSLydia Wang /* Unmute MW4's index 0 */ 4581eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4582eb7188caSLydia Wang { } 4583eb7188caSLydia Wang }; 4584eb7188caSLydia Wang 4585eb7188caSLydia Wang 4586eb7188caSLydia Wang static struct hda_verb vt1718S_uniwill_init_verbs[] = { 4587eb7188caSLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 4588eb7188caSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4589eb7188caSLydia Wang {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4590eb7188caSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4591eb7188caSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4592eb7188caSLydia Wang {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4593eb7188caSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4594eb7188caSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4595eb7188caSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4596eb7188caSLydia Wang { } 4597eb7188caSLydia Wang }; 4598eb7188caSLydia Wang 4599eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_playback = { 4600eb7188caSLydia Wang .substreams = 2, 4601eb7188caSLydia Wang .channels_min = 2, 4602eb7188caSLydia Wang .channels_max = 10, 4603eb7188caSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 4604eb7188caSLydia Wang .ops = { 4605eb7188caSLydia Wang .open = via_playback_pcm_open, 4606eb7188caSLydia Wang .prepare = via_playback_multi_pcm_prepare, 4607eb7188caSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 4608eb7188caSLydia Wang .close = via_pcm_open_close, 4609eb7188caSLydia Wang }, 4610eb7188caSLydia Wang }; 4611eb7188caSLydia Wang 4612eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_capture = { 4613eb7188caSLydia Wang .substreams = 2, 4614eb7188caSLydia Wang .channels_min = 2, 4615eb7188caSLydia Wang .channels_max = 2, 4616eb7188caSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 4617eb7188caSLydia Wang .ops = { 4618eb7188caSLydia Wang .open = via_pcm_open_close, 4619eb7188caSLydia Wang .prepare = via_capture_pcm_prepare, 4620eb7188caSLydia Wang .cleanup = via_capture_pcm_cleanup, 4621eb7188caSLydia Wang .close = via_pcm_open_close, 4622eb7188caSLydia Wang }, 4623eb7188caSLydia Wang }; 4624eb7188caSLydia Wang 4625eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_playback = { 4626eb7188caSLydia Wang .substreams = 2, 4627eb7188caSLydia Wang .channels_min = 2, 4628eb7188caSLydia Wang .channels_max = 2, 4629eb7188caSLydia Wang .rates = SNDRV_PCM_RATE_48000, 4630eb7188caSLydia Wang /* NID is set in via_build_pcms */ 4631eb7188caSLydia Wang .ops = { 4632eb7188caSLydia Wang .open = via_dig_playback_pcm_open, 4633eb7188caSLydia Wang .close = via_dig_playback_pcm_close, 4634eb7188caSLydia Wang .prepare = via_dig_playback_pcm_prepare, 4635eb7188caSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 4636eb7188caSLydia Wang }, 4637eb7188caSLydia Wang }; 4638eb7188caSLydia Wang 4639eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_capture = { 4640eb7188caSLydia Wang .substreams = 1, 4641eb7188caSLydia Wang .channels_min = 2, 4642eb7188caSLydia Wang .channels_max = 2, 4643eb7188caSLydia Wang }; 4644eb7188caSLydia Wang 4645eb7188caSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 4646eb7188caSLydia Wang static int vt1718S_auto_fill_dac_nids(struct via_spec *spec, 4647eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4648eb7188caSLydia Wang { 4649eb7188caSLydia Wang int i; 4650eb7188caSLydia Wang hda_nid_t nid; 4651eb7188caSLydia Wang 4652eb7188caSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 4653eb7188caSLydia Wang 4654eb7188caSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 4655eb7188caSLydia Wang 4656eb7188caSLydia Wang for (i = 0; i < 4; i++) { 4657eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4658eb7188caSLydia Wang if (nid) { 4659eb7188caSLydia Wang /* config dac list */ 4660eb7188caSLydia Wang switch (i) { 4661eb7188caSLydia Wang case AUTO_SEQ_FRONT: 4662eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0x8; 4663eb7188caSLydia Wang break; 4664eb7188caSLydia Wang case AUTO_SEQ_CENLFE: 4665eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0xa; 4666eb7188caSLydia Wang break; 4667eb7188caSLydia Wang case AUTO_SEQ_SURROUND: 4668eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0x9; 4669eb7188caSLydia Wang break; 4670eb7188caSLydia Wang case AUTO_SEQ_SIDE: 4671eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0xb; 4672eb7188caSLydia Wang break; 4673eb7188caSLydia Wang } 4674eb7188caSLydia Wang } 4675eb7188caSLydia Wang } 4676eb7188caSLydia Wang 4677eb7188caSLydia Wang return 0; 4678eb7188caSLydia Wang } 4679eb7188caSLydia Wang 4680eb7188caSLydia Wang /* add playback controls from the parsed DAC table */ 4681eb7188caSLydia Wang static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec, 4682eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4683eb7188caSLydia Wang { 4684eb7188caSLydia Wang char name[32]; 4685eb7188caSLydia Wang static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 4686eb7188caSLydia Wang hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb}; 4687eb7188caSLydia Wang hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27}; 4688eb7188caSLydia Wang hda_nid_t nid, nid_vol, nid_mute = 0; 4689eb7188caSLydia Wang int i, err; 4690eb7188caSLydia Wang 4691eb7188caSLydia Wang for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 4692eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4693eb7188caSLydia Wang 4694eb7188caSLydia Wang if (!nid) 4695eb7188caSLydia Wang continue; 4696eb7188caSLydia Wang nid_vol = nid_vols[i]; 4697eb7188caSLydia Wang nid_mute = nid_mutes[i]; 4698eb7188caSLydia Wang 4699eb7188caSLydia Wang if (i == AUTO_SEQ_CENLFE) { 4700eb7188caSLydia Wang /* Center/LFE */ 4701eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4702eb7188caSLydia Wang "Center Playback Volume", 4703eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 4704eb7188caSLydia Wang HDA_OUTPUT)); 4705eb7188caSLydia Wang if (err < 0) 4706eb7188caSLydia Wang return err; 4707eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4708eb7188caSLydia Wang "LFE Playback Volume", 4709eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 4710eb7188caSLydia Wang HDA_OUTPUT)); 4711eb7188caSLydia Wang if (err < 0) 4712eb7188caSLydia Wang return err; 4713eb7188caSLydia Wang err = via_add_control( 4714eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4715eb7188caSLydia Wang "Center Playback Switch", 4716eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 4717eb7188caSLydia Wang HDA_OUTPUT)); 4718eb7188caSLydia Wang if (err < 0) 4719eb7188caSLydia Wang return err; 4720eb7188caSLydia Wang err = via_add_control( 4721eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4722eb7188caSLydia Wang "LFE Playback Switch", 4723eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 4724eb7188caSLydia Wang HDA_OUTPUT)); 4725eb7188caSLydia Wang if (err < 0) 4726eb7188caSLydia Wang return err; 4727eb7188caSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 4728eb7188caSLydia Wang /* Front */ 4729eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4730eb7188caSLydia Wang err = via_add_control( 4731eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4732eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4733eb7188caSLydia Wang if (err < 0) 4734eb7188caSLydia Wang return err; 4735eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4736eb7188caSLydia Wang err = via_add_control( 4737eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4738eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4739eb7188caSLydia Wang HDA_OUTPUT)); 4740eb7188caSLydia Wang if (err < 0) 4741eb7188caSLydia Wang return err; 4742eb7188caSLydia Wang } else { 4743eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4744eb7188caSLydia Wang err = via_add_control( 4745eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4746eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4747eb7188caSLydia Wang if (err < 0) 4748eb7188caSLydia Wang return err; 4749eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4750eb7188caSLydia Wang err = via_add_control( 4751eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4752eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4753eb7188caSLydia Wang HDA_OUTPUT)); 4754eb7188caSLydia Wang if (err < 0) 4755eb7188caSLydia Wang return err; 4756eb7188caSLydia Wang } 4757eb7188caSLydia Wang } 4758eb7188caSLydia Wang return 0; 4759eb7188caSLydia Wang } 4760eb7188caSLydia Wang 4761eb7188caSLydia Wang static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4762eb7188caSLydia Wang { 4763eb7188caSLydia Wang int err; 4764eb7188caSLydia Wang 4765eb7188caSLydia Wang if (!pin) 4766eb7188caSLydia Wang return 0; 4767eb7188caSLydia Wang 4768eb7188caSLydia Wang spec->multiout.hp_nid = 0xc; /* AOW4 */ 4769eb7188caSLydia Wang spec->hp_independent_mode_index = 1; 4770eb7188caSLydia Wang 4771eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4772eb7188caSLydia Wang "Headphone Playback Volume", 4773eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT)); 4774eb7188caSLydia Wang if (err < 0) 4775eb7188caSLydia Wang return err; 4776eb7188caSLydia Wang 4777eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4778eb7188caSLydia Wang "Headphone Playback Switch", 4779eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4780eb7188caSLydia Wang if (err < 0) 4781eb7188caSLydia Wang return err; 4782eb7188caSLydia Wang 4783eb7188caSLydia Wang create_hp_imux(spec); 4784eb7188caSLydia Wang return 0; 4785eb7188caSLydia Wang } 4786eb7188caSLydia Wang 4787eb7188caSLydia Wang /* create playback/capture controls for input pins */ 4788eb7188caSLydia Wang static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec, 4789eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4790eb7188caSLydia Wang { 4791eb7188caSLydia Wang static char *labels[] = { 4792eb7188caSLydia Wang "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 4793eb7188caSLydia Wang }; 4794eb7188caSLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 4795eb7188caSLydia Wang int i, err, idx = 0; 4796eb7188caSLydia Wang 4797eb7188caSLydia Wang /* for internal loopback recording select */ 4798eb7188caSLydia Wang imux->items[imux->num_items].label = "Stereo Mixer"; 4799eb7188caSLydia Wang imux->items[imux->num_items].index = 5; 4800eb7188caSLydia Wang imux->num_items++; 4801eb7188caSLydia Wang 4802eb7188caSLydia Wang for (i = 0; i < AUTO_PIN_LAST; i++) { 4803eb7188caSLydia Wang if (!cfg->input_pins[i]) 4804eb7188caSLydia Wang continue; 4805eb7188caSLydia Wang 4806eb7188caSLydia Wang switch (cfg->input_pins[i]) { 4807eb7188caSLydia Wang case 0x2b: /* Mic */ 4808eb7188caSLydia Wang idx = 1; 4809eb7188caSLydia Wang break; 4810eb7188caSLydia Wang 4811eb7188caSLydia Wang case 0x2a: /* Line In */ 4812eb7188caSLydia Wang idx = 2; 4813eb7188caSLydia Wang break; 4814eb7188caSLydia Wang 4815eb7188caSLydia Wang case 0x29: /* Front Mic */ 4816eb7188caSLydia Wang idx = 3; 4817eb7188caSLydia Wang break; 4818eb7188caSLydia Wang 4819eb7188caSLydia Wang case 0x2c: /* CD */ 4820eb7188caSLydia Wang idx = 0; 4821eb7188caSLydia Wang break; 4822eb7188caSLydia Wang } 4823eb7188caSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x21); 4824eb7188caSLydia Wang if (err < 0) 4825eb7188caSLydia Wang return err; 4826eb7188caSLydia Wang imux->items[imux->num_items].label = labels[i]; 4827eb7188caSLydia Wang imux->items[imux->num_items].index = idx; 4828eb7188caSLydia Wang imux->num_items++; 4829eb7188caSLydia Wang } 4830eb7188caSLydia Wang return 0; 4831eb7188caSLydia Wang } 4832eb7188caSLydia Wang 4833eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec) 4834eb7188caSLydia Wang { 4835eb7188caSLydia Wang struct via_spec *spec = codec->spec; 4836eb7188caSLydia Wang int err; 4837eb7188caSLydia Wang 4838eb7188caSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4839eb7188caSLydia Wang 4840eb7188caSLydia Wang if (err < 0) 4841eb7188caSLydia Wang return err; 4842eb7188caSLydia Wang err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg); 4843eb7188caSLydia Wang if (err < 0) 4844eb7188caSLydia Wang return err; 4845eb7188caSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4846eb7188caSLydia Wang return 0; /* can't find valid BIOS pin config */ 4847eb7188caSLydia Wang 4848eb7188caSLydia Wang err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg); 4849eb7188caSLydia Wang if (err < 0) 4850eb7188caSLydia Wang return err; 4851eb7188caSLydia Wang err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4852eb7188caSLydia Wang if (err < 0) 4853eb7188caSLydia Wang return err; 4854eb7188caSLydia Wang err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg); 4855eb7188caSLydia Wang if (err < 0) 4856eb7188caSLydia Wang return err; 4857eb7188caSLydia Wang 4858eb7188caSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4859eb7188caSLydia Wang 4860eb7188caSLydia Wang fill_dig_outs(codec); 4861eb7188caSLydia Wang 4862eb7188caSLydia Wang if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) 4863eb7188caSLydia Wang spec->dig_in_nid = 0x13; 4864eb7188caSLydia Wang 4865eb7188caSLydia Wang if (spec->kctls.list) 4866eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 4867eb7188caSLydia Wang 4868eb7188caSLydia Wang spec->input_mux = &spec->private_imux[0]; 4869eb7188caSLydia Wang 4870eb7188caSLydia Wang if (spec->hp_mux) 4871eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = via_hp_mixer; 4872eb7188caSLydia Wang 4873eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = via_smart51_mixer; 4874eb7188caSLydia Wang 4875eb7188caSLydia Wang return 1; 4876eb7188caSLydia Wang } 4877eb7188caSLydia Wang 4878eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4879eb7188caSLydia Wang static struct hda_amp_list vt1718S_loopbacks[] = { 4880eb7188caSLydia Wang { 0x21, HDA_INPUT, 1 }, 4881eb7188caSLydia Wang { 0x21, HDA_INPUT, 2 }, 4882eb7188caSLydia Wang { 0x21, HDA_INPUT, 3 }, 4883eb7188caSLydia Wang { 0x21, HDA_INPUT, 4 }, 4884eb7188caSLydia Wang { } /* end */ 4885eb7188caSLydia Wang }; 4886eb7188caSLydia Wang #endif 4887eb7188caSLydia Wang 4888eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 4889eb7188caSLydia Wang { 4890eb7188caSLydia Wang struct via_spec *spec; 4891eb7188caSLydia Wang int err; 4892eb7188caSLydia Wang 4893eb7188caSLydia Wang /* create a codec specific record */ 4894eb7188caSLydia Wang spec = kzalloc(sizeof(*spec), GFP_KERNEL); 4895eb7188caSLydia Wang if (spec == NULL) 4896eb7188caSLydia Wang return -ENOMEM; 4897eb7188caSLydia Wang 4898eb7188caSLydia Wang codec->spec = spec; 4899eb7188caSLydia Wang 4900eb7188caSLydia Wang /* automatic parse from the BIOS config */ 4901eb7188caSLydia Wang err = vt1718S_parse_auto_config(codec); 4902eb7188caSLydia Wang if (err < 0) { 4903eb7188caSLydia Wang via_free(codec); 4904eb7188caSLydia Wang return err; 4905eb7188caSLydia Wang } else if (!err) { 4906eb7188caSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 4907eb7188caSLydia Wang "from BIOS. Using genenic mode...\n"); 4908eb7188caSLydia Wang } 4909eb7188caSLydia Wang 4910eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; 4911eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; 4912eb7188caSLydia Wang 4913bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060441) 4914bb3c6bfcSLydia Wang spec->stream_name_analog = "VT2020 Analog"; 4915bb3c6bfcSLydia Wang else if (codec->vendor_id == 0x11064441) 4916bb3c6bfcSLydia Wang spec->stream_name_analog = "VT1828S Analog"; 4917bb3c6bfcSLydia Wang else 4918eb7188caSLydia Wang spec->stream_name_analog = "VT1718S Analog"; 4919eb7188caSLydia Wang spec->stream_analog_playback = &vt1718S_pcm_analog_playback; 4920eb7188caSLydia Wang spec->stream_analog_capture = &vt1718S_pcm_analog_capture; 4921eb7188caSLydia Wang 4922bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060441) 4923bb3c6bfcSLydia Wang spec->stream_name_digital = "VT2020 Digital"; 4924bb3c6bfcSLydia Wang else if (codec->vendor_id == 0x11064441) 4925bb3c6bfcSLydia Wang spec->stream_name_digital = "VT1828S Digital"; 4926bb3c6bfcSLydia Wang else 4927eb7188caSLydia Wang spec->stream_name_digital = "VT1718S Digital"; 4928eb7188caSLydia Wang spec->stream_digital_playback = &vt1718S_pcm_digital_playback; 4929bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) 4930eb7188caSLydia Wang spec->stream_digital_capture = &vt1718S_pcm_digital_capture; 4931eb7188caSLydia Wang 4932eb7188caSLydia Wang if (!spec->adc_nids && spec->input_mux) { 4933eb7188caSLydia Wang spec->adc_nids = vt1718S_adc_nids; 4934eb7188caSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids); 4935eb7188caSLydia Wang get_mux_nids(codec); 4936bb3c6bfcSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 4937bb3c6bfcSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 4938eb7188caSLydia Wang spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; 4939eb7188caSLydia Wang spec->num_mixers++; 4940eb7188caSLydia Wang } 4941eb7188caSLydia Wang 4942eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 4943eb7188caSLydia Wang 4944eb7188caSLydia Wang codec->patch_ops.init = via_auto_init; 4945eb7188caSLydia Wang codec->patch_ops.unsol_event = via_unsol_event, 4946eb7188caSLydia Wang 4947eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4948eb7188caSLydia Wang spec->loopback.amplist = vt1718S_loopbacks; 4949eb7188caSLydia Wang #endif 4950eb7188caSLydia Wang 4951eb7188caSLydia Wang return 0; 4952eb7188caSLydia Wang } 4953f3db423dSLydia Wang 4954f3db423dSLydia Wang /* Patch for VT1716S */ 4955f3db423dSLydia Wang 4956f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 4957f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 4958f3db423dSLydia Wang { 4959f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 4960f3db423dSLydia Wang uinfo->count = 1; 4961f3db423dSLydia Wang uinfo->value.integer.min = 0; 4962f3db423dSLydia Wang uinfo->value.integer.max = 1; 4963f3db423dSLydia Wang return 0; 4964f3db423dSLydia Wang } 4965f3db423dSLydia Wang 4966f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 4967f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 4968f3db423dSLydia Wang { 4969f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4970f3db423dSLydia Wang int index = 0; 4971f3db423dSLydia Wang 4972f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 4973f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 4974f3db423dSLydia Wang if (index != -1) 4975f3db423dSLydia Wang *ucontrol->value.integer.value = index; 4976f3db423dSLydia Wang 4977f3db423dSLydia Wang return 0; 4978f3db423dSLydia Wang } 4979f3db423dSLydia Wang 4980f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 4981f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 4982f3db423dSLydia Wang { 4983f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4984f3db423dSLydia Wang struct via_spec *spec = codec->spec; 4985f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 4986f3db423dSLydia Wang 4987f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 4988f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 4989f3db423dSLydia Wang spec->dmic_enabled = index; 4990f3db423dSLydia Wang set_jack_power_state(codec); 4991f3db423dSLydia Wang 4992f3db423dSLydia Wang return 1; 4993f3db423dSLydia Wang } 4994f3db423dSLydia Wang 4995f3db423dSLydia Wang /* capture mixer elements */ 4996f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_capture_mixer[] = { 4997f3db423dSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 4998f3db423dSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 4999f3db423dSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 5000f3db423dSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 5001f3db423dSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 5002f3db423dSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 5003f3db423dSLydia Wang HDA_INPUT), 5004f3db423dSLydia Wang { 5005f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5006f3db423dSLydia Wang .name = "Input Source", 5007f3db423dSLydia Wang .count = 1, 5008f3db423dSLydia Wang .info = via_mux_enum_info, 5009f3db423dSLydia Wang .get = via_mux_enum_get, 5010f3db423dSLydia Wang .put = via_mux_enum_put, 5011f3db423dSLydia Wang }, 5012f3db423dSLydia Wang { } /* end */ 5013f3db423dSLydia Wang }; 5014f3db423dSLydia Wang 5015f3db423dSLydia Wang static struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 5016f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 5017f3db423dSLydia Wang { 5018f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5019f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 5020f3db423dSLydia Wang .count = 1, 5021f3db423dSLydia Wang .info = vt1716s_dmic_info, 5022f3db423dSLydia Wang .get = vt1716s_dmic_get, 5023f3db423dSLydia Wang .put = vt1716s_dmic_put, 5024f3db423dSLydia Wang }, 5025f3db423dSLydia Wang {} /* end */ 5026f3db423dSLydia Wang }; 5027f3db423dSLydia Wang 5028f3db423dSLydia Wang 5029f3db423dSLydia Wang /* mono-out mixer elements */ 5030f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 5031f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 5032f3db423dSLydia Wang { } /* end */ 5033f3db423dSLydia Wang }; 5034f3db423dSLydia Wang 5035f3db423dSLydia Wang static struct hda_verb vt1716S_volume_init_verbs[] = { 5036f3db423dSLydia Wang /* 5037f3db423dSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 5038f3db423dSLydia Wang */ 5039f3db423dSLydia Wang {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5040f3db423dSLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5041f3db423dSLydia Wang 5042f3db423dSLydia Wang 5043f3db423dSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 5044f3db423dSLydia Wang * mixer widget 5045f3db423dSLydia Wang */ 5046f3db423dSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 5047f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 5048f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 5049f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 5050f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 5051f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 5052f3db423dSLydia Wang 5053f3db423dSLydia Wang /* MUX Indices: Stereo Mixer = 5 */ 5054f3db423dSLydia Wang {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, 5055f3db423dSLydia Wang 5056f3db423dSLydia Wang /* Setup default input of PW4 to MW0 */ 5057f3db423dSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 5058f3db423dSLydia Wang 5059f3db423dSLydia Wang /* Setup default input of SW1 as MW0 */ 5060f3db423dSLydia Wang {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, 5061f3db423dSLydia Wang 5062f3db423dSLydia Wang /* Setup default input of SW4 as AOW0 */ 5063f3db423dSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 5064f3db423dSLydia Wang 5065f3db423dSLydia Wang /* PW9 PW10 Output enable */ 5066f3db423dSLydia Wang {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 5067f3db423dSLydia Wang {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 5068f3db423dSLydia Wang 5069f3db423dSLydia Wang /* Unmute SW1, PW12 */ 5070f3db423dSLydia Wang {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5071f3db423dSLydia Wang {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 5072f3db423dSLydia Wang /* PW12 Output enable */ 5073f3db423dSLydia Wang {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 5074f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 5075f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 5076f3db423dSLydia Wang /* don't bybass mixer */ 5077f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 5078f3db423dSLydia Wang /* Enable mono output */ 5079f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 5080f3db423dSLydia Wang { } 5081f3db423dSLydia Wang }; 5082f3db423dSLydia Wang 5083f3db423dSLydia Wang 5084f3db423dSLydia Wang static struct hda_verb vt1716S_uniwill_init_verbs[] = { 5085f3db423dSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 5086f3db423dSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 5087f3db423dSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5088f3db423dSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5089f3db423dSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5090f3db423dSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 5091f3db423dSLydia Wang AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, 5092f3db423dSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5093f3db423dSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5094f3db423dSLydia Wang { } 5095f3db423dSLydia Wang }; 5096f3db423dSLydia Wang 5097f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_playback = { 5098f3db423dSLydia Wang .substreams = 2, 5099f3db423dSLydia Wang .channels_min = 2, 5100f3db423dSLydia Wang .channels_max = 6, 5101f3db423dSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 5102f3db423dSLydia Wang .ops = { 5103f3db423dSLydia Wang .open = via_playback_pcm_open, 5104f3db423dSLydia Wang .prepare = via_playback_multi_pcm_prepare, 5105f3db423dSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 5106f3db423dSLydia Wang .close = via_pcm_open_close, 5107f3db423dSLydia Wang }, 5108f3db423dSLydia Wang }; 5109f3db423dSLydia Wang 5110f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_capture = { 5111f3db423dSLydia Wang .substreams = 2, 5112f3db423dSLydia Wang .channels_min = 2, 5113f3db423dSLydia Wang .channels_max = 2, 5114f3db423dSLydia Wang .nid = 0x13, /* NID to query formats and rates */ 5115f3db423dSLydia Wang .ops = { 5116f3db423dSLydia Wang .open = via_pcm_open_close, 5117f3db423dSLydia Wang .prepare = via_capture_pcm_prepare, 5118f3db423dSLydia Wang .cleanup = via_capture_pcm_cleanup, 5119f3db423dSLydia Wang .close = via_pcm_open_close, 5120f3db423dSLydia Wang }, 5121f3db423dSLydia Wang }; 5122f3db423dSLydia Wang 5123f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_digital_playback = { 5124f3db423dSLydia Wang .substreams = 2, 5125f3db423dSLydia Wang .channels_min = 2, 5126f3db423dSLydia Wang .channels_max = 2, 5127f3db423dSLydia Wang .rates = SNDRV_PCM_RATE_48000, 5128f3db423dSLydia Wang /* NID is set in via_build_pcms */ 5129f3db423dSLydia Wang .ops = { 5130f3db423dSLydia Wang .open = via_dig_playback_pcm_open, 5131f3db423dSLydia Wang .close = via_dig_playback_pcm_close, 5132f3db423dSLydia Wang .prepare = via_dig_playback_pcm_prepare, 5133f3db423dSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 5134f3db423dSLydia Wang }, 5135f3db423dSLydia Wang }; 5136f3db423dSLydia Wang 5137f3db423dSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 5138f3db423dSLydia Wang static int vt1716S_auto_fill_dac_nids(struct via_spec *spec, 5139f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5140f3db423dSLydia Wang { int i; 5141f3db423dSLydia Wang hda_nid_t nid; 5142f3db423dSLydia Wang 5143f3db423dSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 5144f3db423dSLydia Wang 5145f3db423dSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 5146f3db423dSLydia Wang 5147f3db423dSLydia Wang for (i = 0; i < 3; i++) { 5148f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 5149f3db423dSLydia Wang if (nid) { 5150f3db423dSLydia Wang /* config dac list */ 5151f3db423dSLydia Wang switch (i) { 5152f3db423dSLydia Wang case AUTO_SEQ_FRONT: 5153f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x10; 5154f3db423dSLydia Wang break; 5155f3db423dSLydia Wang case AUTO_SEQ_CENLFE: 5156f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x25; 5157f3db423dSLydia Wang break; 5158f3db423dSLydia Wang case AUTO_SEQ_SURROUND: 5159f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x11; 5160f3db423dSLydia Wang break; 5161f3db423dSLydia Wang } 5162f3db423dSLydia Wang } 5163f3db423dSLydia Wang } 5164f3db423dSLydia Wang 5165f3db423dSLydia Wang return 0; 5166f3db423dSLydia Wang } 5167f3db423dSLydia Wang 5168f3db423dSLydia Wang /* add playback controls from the parsed DAC table */ 5169f3db423dSLydia Wang static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec, 5170f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5171f3db423dSLydia Wang { 5172f3db423dSLydia Wang char name[32]; 5173f3db423dSLydia Wang static const char *chname[3] = { "Front", "Surround", "C/LFE" }; 5174f3db423dSLydia Wang hda_nid_t nid_vols[] = {0x10, 0x11, 0x25}; 5175f3db423dSLydia Wang hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27}; 5176f3db423dSLydia Wang hda_nid_t nid, nid_vol, nid_mute; 5177f3db423dSLydia Wang int i, err; 5178f3db423dSLydia Wang 5179f3db423dSLydia Wang for (i = 0; i <= AUTO_SEQ_CENLFE; i++) { 5180f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 5181f3db423dSLydia Wang 5182f3db423dSLydia Wang if (!nid) 5183f3db423dSLydia Wang continue; 5184f3db423dSLydia Wang 5185f3db423dSLydia Wang nid_vol = nid_vols[i]; 5186f3db423dSLydia Wang nid_mute = nid_mutes[i]; 5187f3db423dSLydia Wang 5188f3db423dSLydia Wang if (i == AUTO_SEQ_CENLFE) { 5189f3db423dSLydia Wang err = via_add_control( 5190f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 5191f3db423dSLydia Wang "Center Playback Volume", 5192f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); 5193f3db423dSLydia Wang if (err < 0) 5194f3db423dSLydia Wang return err; 5195f3db423dSLydia Wang err = via_add_control( 5196f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 5197f3db423dSLydia Wang "LFE Playback Volume", 5198f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); 5199f3db423dSLydia Wang if (err < 0) 5200f3db423dSLydia Wang return err; 5201f3db423dSLydia Wang err = via_add_control( 5202f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 5203f3db423dSLydia Wang "Center Playback Switch", 5204f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 5205f3db423dSLydia Wang HDA_OUTPUT)); 5206f3db423dSLydia Wang if (err < 0) 5207f3db423dSLydia Wang return err; 5208f3db423dSLydia Wang err = via_add_control( 5209f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 5210f3db423dSLydia Wang "LFE Playback Switch", 5211f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 5212f3db423dSLydia Wang HDA_OUTPUT)); 5213f3db423dSLydia Wang if (err < 0) 5214f3db423dSLydia Wang return err; 5215f3db423dSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 5216f3db423dSLydia Wang 5217f3db423dSLydia Wang err = via_add_control( 5218f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 5219f3db423dSLydia Wang "Master Front Playback Volume", 5220f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 5221f3db423dSLydia Wang if (err < 0) 5222f3db423dSLydia Wang return err; 5223f3db423dSLydia Wang err = via_add_control( 5224f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 5225f3db423dSLydia Wang "Master Front Playback Switch", 5226f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 5227f3db423dSLydia Wang if (err < 0) 5228f3db423dSLydia Wang return err; 5229f3db423dSLydia Wang 5230f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 5231f3db423dSLydia Wang err = via_add_control( 5232f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 5233f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 5234f3db423dSLydia Wang if (err < 0) 5235f3db423dSLydia Wang return err; 5236f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 5237f3db423dSLydia Wang err = via_add_control( 5238f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 5239f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 5240f3db423dSLydia Wang HDA_OUTPUT)); 5241f3db423dSLydia Wang if (err < 0) 5242f3db423dSLydia Wang return err; 5243f3db423dSLydia Wang } else { 5244f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 5245f3db423dSLydia Wang err = via_add_control( 5246f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 5247f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 5248f3db423dSLydia Wang if (err < 0) 5249f3db423dSLydia Wang return err; 5250f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 5251f3db423dSLydia Wang err = via_add_control( 5252f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 5253f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 5254f3db423dSLydia Wang HDA_OUTPUT)); 5255f3db423dSLydia Wang if (err < 0) 5256f3db423dSLydia Wang return err; 5257f3db423dSLydia Wang } 5258f3db423dSLydia Wang } 5259f3db423dSLydia Wang return 0; 5260f3db423dSLydia Wang } 5261f3db423dSLydia Wang 5262f3db423dSLydia Wang static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 5263f3db423dSLydia Wang { 5264f3db423dSLydia Wang int err; 5265f3db423dSLydia Wang 5266f3db423dSLydia Wang if (!pin) 5267f3db423dSLydia Wang return 0; 5268f3db423dSLydia Wang 5269f3db423dSLydia Wang spec->multiout.hp_nid = 0x25; /* AOW3 */ 5270f3db423dSLydia Wang spec->hp_independent_mode_index = 1; 5271f3db423dSLydia Wang 5272f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 5273f3db423dSLydia Wang "Headphone Playback Volume", 5274f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 5275f3db423dSLydia Wang if (err < 0) 5276f3db423dSLydia Wang return err; 5277f3db423dSLydia Wang 5278f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 5279f3db423dSLydia Wang "Headphone Playback Switch", 5280f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 5281f3db423dSLydia Wang if (err < 0) 5282f3db423dSLydia Wang return err; 5283f3db423dSLydia Wang 5284f3db423dSLydia Wang create_hp_imux(spec); 5285f3db423dSLydia Wang return 0; 5286f3db423dSLydia Wang } 5287f3db423dSLydia Wang 5288f3db423dSLydia Wang /* create playback/capture controls for input pins */ 5289f3db423dSLydia Wang static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec, 5290f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5291f3db423dSLydia Wang { 5292f3db423dSLydia Wang static char *labels[] = { 5293f3db423dSLydia Wang "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 5294f3db423dSLydia Wang }; 5295f3db423dSLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 5296f3db423dSLydia Wang int i, err, idx = 0; 5297f3db423dSLydia Wang 5298f3db423dSLydia Wang /* for internal loopback recording select */ 5299f3db423dSLydia Wang imux->items[imux->num_items].label = "Stereo Mixer"; 5300f3db423dSLydia Wang imux->items[imux->num_items].index = 5; 5301f3db423dSLydia Wang imux->num_items++; 5302f3db423dSLydia Wang 5303f3db423dSLydia Wang for (i = 0; i < AUTO_PIN_LAST; i++) { 5304f3db423dSLydia Wang if (!cfg->input_pins[i]) 5305f3db423dSLydia Wang continue; 5306f3db423dSLydia Wang 5307f3db423dSLydia Wang switch (cfg->input_pins[i]) { 5308f3db423dSLydia Wang case 0x1a: /* Mic */ 5309f3db423dSLydia Wang idx = 2; 5310f3db423dSLydia Wang break; 5311f3db423dSLydia Wang 5312f3db423dSLydia Wang case 0x1b: /* Line In */ 5313f3db423dSLydia Wang idx = 3; 5314f3db423dSLydia Wang break; 5315f3db423dSLydia Wang 5316f3db423dSLydia Wang case 0x1e: /* Front Mic */ 5317f3db423dSLydia Wang idx = 4; 5318f3db423dSLydia Wang break; 5319f3db423dSLydia Wang 5320f3db423dSLydia Wang case 0x1f: /* CD */ 5321f3db423dSLydia Wang idx = 1; 5322f3db423dSLydia Wang break; 5323f3db423dSLydia Wang } 5324f3db423dSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x16); 5325f3db423dSLydia Wang if (err < 0) 5326f3db423dSLydia Wang return err; 5327f3db423dSLydia Wang imux->items[imux->num_items].label = labels[i]; 5328f3db423dSLydia Wang imux->items[imux->num_items].index = idx-1; 5329f3db423dSLydia Wang imux->num_items++; 5330f3db423dSLydia Wang } 5331f3db423dSLydia Wang return 0; 5332f3db423dSLydia Wang } 5333f3db423dSLydia Wang 5334f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec) 5335f3db423dSLydia Wang { 5336f3db423dSLydia Wang struct via_spec *spec = codec->spec; 5337f3db423dSLydia Wang int err; 5338f3db423dSLydia Wang 5339f3db423dSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 5340f3db423dSLydia Wang if (err < 0) 5341f3db423dSLydia Wang return err; 5342f3db423dSLydia Wang err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg); 5343f3db423dSLydia Wang if (err < 0) 5344f3db423dSLydia Wang return err; 5345f3db423dSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 5346f3db423dSLydia Wang return 0; /* can't find valid BIOS pin config */ 5347f3db423dSLydia Wang 5348f3db423dSLydia Wang err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg); 5349f3db423dSLydia Wang if (err < 0) 5350f3db423dSLydia Wang return err; 5351f3db423dSLydia Wang err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 5352f3db423dSLydia Wang if (err < 0) 5353f3db423dSLydia Wang return err; 5354f3db423dSLydia Wang err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg); 5355f3db423dSLydia Wang if (err < 0) 5356f3db423dSLydia Wang return err; 5357f3db423dSLydia Wang 5358f3db423dSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 5359f3db423dSLydia Wang 5360f3db423dSLydia Wang fill_dig_outs(codec); 5361f3db423dSLydia Wang 5362f3db423dSLydia Wang if (spec->kctls.list) 5363f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 5364f3db423dSLydia Wang 5365f3db423dSLydia Wang spec->input_mux = &spec->private_imux[0]; 5366f3db423dSLydia Wang 5367f3db423dSLydia Wang if (spec->hp_mux) 5368f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = via_hp_mixer; 5369f3db423dSLydia Wang 5370f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = via_smart51_mixer; 5371f3db423dSLydia Wang 5372f3db423dSLydia Wang return 1; 5373f3db423dSLydia Wang } 5374f3db423dSLydia Wang 5375f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5376f3db423dSLydia Wang static struct hda_amp_list vt1716S_loopbacks[] = { 5377f3db423dSLydia Wang { 0x16, HDA_INPUT, 1 }, 5378f3db423dSLydia Wang { 0x16, HDA_INPUT, 2 }, 5379f3db423dSLydia Wang { 0x16, HDA_INPUT, 3 }, 5380f3db423dSLydia Wang { 0x16, HDA_INPUT, 4 }, 5381f3db423dSLydia Wang { } /* end */ 5382f3db423dSLydia Wang }; 5383f3db423dSLydia Wang #endif 5384f3db423dSLydia Wang 5385f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 5386f3db423dSLydia Wang { 5387f3db423dSLydia Wang struct via_spec *spec; 5388f3db423dSLydia Wang int err; 5389f3db423dSLydia Wang 5390f3db423dSLydia Wang /* create a codec specific record */ 5391f3db423dSLydia Wang spec = kzalloc(sizeof(*spec), GFP_KERNEL); 5392f3db423dSLydia Wang if (spec == NULL) 5393f3db423dSLydia Wang return -ENOMEM; 5394f3db423dSLydia Wang 5395f3db423dSLydia Wang codec->spec = spec; 5396f3db423dSLydia Wang 5397f3db423dSLydia Wang /* automatic parse from the BIOS config */ 5398f3db423dSLydia Wang err = vt1716S_parse_auto_config(codec); 5399f3db423dSLydia Wang if (err < 0) { 5400f3db423dSLydia Wang via_free(codec); 5401f3db423dSLydia Wang return err; 5402f3db423dSLydia Wang } else if (!err) { 5403f3db423dSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 5404f3db423dSLydia Wang "from BIOS. Using genenic mode...\n"); 5405f3db423dSLydia Wang } 5406f3db423dSLydia Wang 5407f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; 5408f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; 5409f3db423dSLydia Wang 5410f3db423dSLydia Wang spec->stream_name_analog = "VT1716S Analog"; 5411f3db423dSLydia Wang spec->stream_analog_playback = &vt1716S_pcm_analog_playback; 5412f3db423dSLydia Wang spec->stream_analog_capture = &vt1716S_pcm_analog_capture; 5413f3db423dSLydia Wang 5414f3db423dSLydia Wang spec->stream_name_digital = "VT1716S Digital"; 5415f3db423dSLydia Wang spec->stream_digital_playback = &vt1716S_pcm_digital_playback; 5416f3db423dSLydia Wang 5417f3db423dSLydia Wang if (!spec->adc_nids && spec->input_mux) { 5418f3db423dSLydia Wang spec->adc_nids = vt1716S_adc_nids; 5419f3db423dSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids); 5420f3db423dSLydia Wang get_mux_nids(codec); 5421f3db423dSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 5422f3db423dSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 5423f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; 5424f3db423dSLydia Wang spec->num_mixers++; 5425f3db423dSLydia Wang } 5426f3db423dSLydia Wang 5427f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 5428f3db423dSLydia Wang spec->num_mixers++; 5429f3db423dSLydia Wang 5430f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 5431f3db423dSLydia Wang 5432f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 5433f3db423dSLydia Wang 5434f3db423dSLydia Wang codec->patch_ops.init = via_auto_init; 5435f3db423dSLydia Wang codec->patch_ops.unsol_event = via_unsol_event, 5436f3db423dSLydia Wang 5437f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5438f3db423dSLydia Wang spec->loopback.amplist = vt1716S_loopbacks; 5439f3db423dSLydia Wang #endif 5440f3db423dSLydia Wang 5441f3db423dSLydia Wang return 0; 5442f3db423dSLydia Wang } 544325eaba2fSLydia Wang 544425eaba2fSLydia Wang /* for vt2002P */ 544525eaba2fSLydia Wang 544625eaba2fSLydia Wang /* capture mixer elements */ 544725eaba2fSLydia Wang static struct snd_kcontrol_new vt2002P_capture_mixer[] = { 544825eaba2fSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 544925eaba2fSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 545025eaba2fSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 545125eaba2fSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 545225eaba2fSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 545325eaba2fSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 545425eaba2fSLydia Wang HDA_INPUT), 545525eaba2fSLydia Wang { 545625eaba2fSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 545725eaba2fSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 545825eaba2fSLydia Wang * So call somewhat different.. 545925eaba2fSLydia Wang */ 546025eaba2fSLydia Wang /* .name = "Capture Source", */ 546125eaba2fSLydia Wang .name = "Input Source", 546225eaba2fSLydia Wang .count = 2, 546325eaba2fSLydia Wang .info = via_mux_enum_info, 546425eaba2fSLydia Wang .get = via_mux_enum_get, 546525eaba2fSLydia Wang .put = via_mux_enum_put, 546625eaba2fSLydia Wang }, 546725eaba2fSLydia Wang { } /* end */ 546825eaba2fSLydia Wang }; 546925eaba2fSLydia Wang 547025eaba2fSLydia Wang static struct hda_verb vt2002P_volume_init_verbs[] = { 547125eaba2fSLydia Wang /* 547225eaba2fSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 547325eaba2fSLydia Wang */ 547425eaba2fSLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 547525eaba2fSLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 547625eaba2fSLydia Wang 547725eaba2fSLydia Wang 547825eaba2fSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 547925eaba2fSLydia Wang * mixer widget 548025eaba2fSLydia Wang */ 548125eaba2fSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 548225eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 548325eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 548425eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 548525eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 548625eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 548725eaba2fSLydia Wang 548825eaba2fSLydia Wang /* MUX Indices: Mic = 0 */ 548925eaba2fSLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 549025eaba2fSLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 549125eaba2fSLydia Wang 549225eaba2fSLydia Wang /* PW9 Output enable */ 549325eaba2fSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 549425eaba2fSLydia Wang 549525eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 549625eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 549725eaba2fSLydia Wang 549825eaba2fSLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 549925eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 550025eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 550125eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 550225eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 550325eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 550425eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 550525eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 550625eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 550725eaba2fSLydia Wang 550825eaba2fSLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 550925eaba2fSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 551025eaba2fSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 551125eaba2fSLydia Wang {0x37, AC_VERB_SET_CONNECT_SEL, 0}, 551225eaba2fSLydia Wang {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, 551325eaba2fSLydia Wang 551425eaba2fSLydia Wang /* set PW0 index=0 (MW0) */ 551525eaba2fSLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 551625eaba2fSLydia Wang 551725eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 551825eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 551925eaba2fSLydia Wang { } 552025eaba2fSLydia Wang }; 552125eaba2fSLydia Wang 552225eaba2fSLydia Wang 552325eaba2fSLydia Wang static struct hda_verb vt2002P_uniwill_init_verbs[] = { 552425eaba2fSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 552525eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 552625eaba2fSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, 552725eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 552825eaba2fSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 552925eaba2fSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 553025eaba2fSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 553125eaba2fSLydia Wang { } 553225eaba2fSLydia Wang }; 553325eaba2fSLydia Wang 553425eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_playback = { 553525eaba2fSLydia Wang .substreams = 2, 553625eaba2fSLydia Wang .channels_min = 2, 553725eaba2fSLydia Wang .channels_max = 2, 553825eaba2fSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 553925eaba2fSLydia Wang .ops = { 554025eaba2fSLydia Wang .open = via_playback_pcm_open, 554125eaba2fSLydia Wang .prepare = via_playback_multi_pcm_prepare, 554225eaba2fSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 554325eaba2fSLydia Wang .close = via_pcm_open_close, 554425eaba2fSLydia Wang }, 554525eaba2fSLydia Wang }; 554625eaba2fSLydia Wang 554725eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_capture = { 554825eaba2fSLydia Wang .substreams = 2, 554925eaba2fSLydia Wang .channels_min = 2, 555025eaba2fSLydia Wang .channels_max = 2, 555125eaba2fSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 555225eaba2fSLydia Wang .ops = { 555325eaba2fSLydia Wang .open = via_pcm_open_close, 555425eaba2fSLydia Wang .prepare = via_capture_pcm_prepare, 555525eaba2fSLydia Wang .cleanup = via_capture_pcm_cleanup, 555625eaba2fSLydia Wang .close = via_pcm_open_close, 555725eaba2fSLydia Wang }, 555825eaba2fSLydia Wang }; 555925eaba2fSLydia Wang 556025eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_digital_playback = { 556125eaba2fSLydia Wang .substreams = 1, 556225eaba2fSLydia Wang .channels_min = 2, 556325eaba2fSLydia Wang .channels_max = 2, 556425eaba2fSLydia Wang .rates = SNDRV_PCM_RATE_48000, 556525eaba2fSLydia Wang /* NID is set in via_build_pcms */ 556625eaba2fSLydia Wang .ops = { 556725eaba2fSLydia Wang .open = via_dig_playback_pcm_open, 556825eaba2fSLydia Wang .close = via_dig_playback_pcm_close, 556925eaba2fSLydia Wang .prepare = via_dig_playback_pcm_prepare, 557025eaba2fSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 557125eaba2fSLydia Wang }, 557225eaba2fSLydia Wang }; 557325eaba2fSLydia Wang 557425eaba2fSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 557525eaba2fSLydia Wang static int vt2002P_auto_fill_dac_nids(struct via_spec *spec, 557625eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 557725eaba2fSLydia Wang { 557825eaba2fSLydia Wang spec->multiout.num_dacs = 1; 557925eaba2fSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 558025eaba2fSLydia Wang if (cfg->line_out_pins[0]) 558125eaba2fSLydia Wang spec->multiout.dac_nids[0] = 0x8; 558225eaba2fSLydia Wang return 0; 558325eaba2fSLydia Wang } 558425eaba2fSLydia Wang 558525eaba2fSLydia Wang /* add playback controls from the parsed DAC table */ 558625eaba2fSLydia Wang static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec, 558725eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 558825eaba2fSLydia Wang { 558925eaba2fSLydia Wang int err; 559025eaba2fSLydia Wang 559125eaba2fSLydia Wang if (!cfg->line_out_pins[0]) 559225eaba2fSLydia Wang return -1; 559325eaba2fSLydia Wang 559425eaba2fSLydia Wang 559525eaba2fSLydia Wang /* Line-Out: PortE */ 559625eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 559725eaba2fSLydia Wang "Master Front Playback Volume", 559825eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); 559925eaba2fSLydia Wang if (err < 0) 560025eaba2fSLydia Wang return err; 560125eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, 560225eaba2fSLydia Wang "Master Front Playback Switch", 560325eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT)); 560425eaba2fSLydia Wang if (err < 0) 560525eaba2fSLydia Wang return err; 560625eaba2fSLydia Wang 560725eaba2fSLydia Wang return 0; 560825eaba2fSLydia Wang } 560925eaba2fSLydia Wang 561025eaba2fSLydia Wang static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 561125eaba2fSLydia Wang { 561225eaba2fSLydia Wang int err; 561325eaba2fSLydia Wang 561425eaba2fSLydia Wang if (!pin) 561525eaba2fSLydia Wang return 0; 561625eaba2fSLydia Wang 561725eaba2fSLydia Wang spec->multiout.hp_nid = 0x9; 561825eaba2fSLydia Wang spec->hp_independent_mode_index = 1; 561925eaba2fSLydia Wang 562025eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 562125eaba2fSLydia Wang "Headphone Playback Volume", 562225eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL( 562325eaba2fSLydia Wang spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); 562425eaba2fSLydia Wang if (err < 0) 562525eaba2fSLydia Wang return err; 562625eaba2fSLydia Wang 562725eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 562825eaba2fSLydia Wang "Headphone Playback Switch", 562925eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 563025eaba2fSLydia Wang if (err < 0) 563125eaba2fSLydia Wang return err; 563225eaba2fSLydia Wang 563325eaba2fSLydia Wang create_hp_imux(spec); 563425eaba2fSLydia Wang return 0; 563525eaba2fSLydia Wang } 563625eaba2fSLydia Wang 563725eaba2fSLydia Wang /* create playback/capture controls for input pins */ 563825eaba2fSLydia Wang static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec, 563925eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 564025eaba2fSLydia Wang { 564125eaba2fSLydia Wang static char *labels[] = { 564225eaba2fSLydia Wang "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 564325eaba2fSLydia Wang }; 564425eaba2fSLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 564525eaba2fSLydia Wang int i, err, idx = 0; 564625eaba2fSLydia Wang 564725eaba2fSLydia Wang for (i = 0; i < AUTO_PIN_LAST; i++) { 564825eaba2fSLydia Wang if (!cfg->input_pins[i]) 564925eaba2fSLydia Wang continue; 565025eaba2fSLydia Wang 565125eaba2fSLydia Wang switch (cfg->input_pins[i]) { 565225eaba2fSLydia Wang case 0x2b: /* Mic */ 565325eaba2fSLydia Wang idx = 0; 565425eaba2fSLydia Wang break; 565525eaba2fSLydia Wang 565625eaba2fSLydia Wang case 0x2a: /* Line In */ 565725eaba2fSLydia Wang idx = 1; 565825eaba2fSLydia Wang break; 565925eaba2fSLydia Wang 566025eaba2fSLydia Wang case 0x29: /* Front Mic */ 566125eaba2fSLydia Wang idx = 2; 566225eaba2fSLydia Wang break; 566325eaba2fSLydia Wang } 566425eaba2fSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x21); 566525eaba2fSLydia Wang if (err < 0) 566625eaba2fSLydia Wang return err; 566725eaba2fSLydia Wang imux->items[imux->num_items].label = labels[i]; 566825eaba2fSLydia Wang imux->items[imux->num_items].index = idx; 566925eaba2fSLydia Wang imux->num_items++; 567025eaba2fSLydia Wang } 567125eaba2fSLydia Wang 567225eaba2fSLydia Wang /* build volume/mute control of loopback */ 567325eaba2fSLydia Wang err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21); 567425eaba2fSLydia Wang if (err < 0) 567525eaba2fSLydia Wang return err; 567625eaba2fSLydia Wang 567725eaba2fSLydia Wang /* for internal loopback recording select */ 567825eaba2fSLydia Wang imux->items[imux->num_items].label = "Stereo Mixer"; 567925eaba2fSLydia Wang imux->items[imux->num_items].index = 3; 568025eaba2fSLydia Wang imux->num_items++; 568125eaba2fSLydia Wang 568225eaba2fSLydia Wang /* for digital mic select */ 568325eaba2fSLydia Wang imux->items[imux->num_items].label = "Digital Mic"; 568425eaba2fSLydia Wang imux->items[imux->num_items].index = 4; 568525eaba2fSLydia Wang imux->num_items++; 568625eaba2fSLydia Wang 568725eaba2fSLydia Wang return 0; 568825eaba2fSLydia Wang } 568925eaba2fSLydia Wang 569025eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec) 569125eaba2fSLydia Wang { 569225eaba2fSLydia Wang struct via_spec *spec = codec->spec; 569325eaba2fSLydia Wang int err; 569425eaba2fSLydia Wang 569525eaba2fSLydia Wang 569625eaba2fSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 569725eaba2fSLydia Wang if (err < 0) 569825eaba2fSLydia Wang return err; 569925eaba2fSLydia Wang 570025eaba2fSLydia Wang err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg); 570125eaba2fSLydia Wang if (err < 0) 570225eaba2fSLydia Wang return err; 570325eaba2fSLydia Wang 570425eaba2fSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 570525eaba2fSLydia Wang return 0; /* can't find valid BIOS pin config */ 570625eaba2fSLydia Wang 570725eaba2fSLydia Wang err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg); 570825eaba2fSLydia Wang if (err < 0) 570925eaba2fSLydia Wang return err; 571025eaba2fSLydia Wang err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 571125eaba2fSLydia Wang if (err < 0) 571225eaba2fSLydia Wang return err; 571325eaba2fSLydia Wang err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg); 571425eaba2fSLydia Wang if (err < 0) 571525eaba2fSLydia Wang return err; 571625eaba2fSLydia Wang 571725eaba2fSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 571825eaba2fSLydia Wang 571925eaba2fSLydia Wang fill_dig_outs(codec); 572025eaba2fSLydia Wang 572125eaba2fSLydia Wang if (spec->kctls.list) 572225eaba2fSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 572325eaba2fSLydia Wang 572425eaba2fSLydia Wang spec->input_mux = &spec->private_imux[0]; 572525eaba2fSLydia Wang 572625eaba2fSLydia Wang if (spec->hp_mux) 572725eaba2fSLydia Wang spec->mixers[spec->num_mixers++] = via_hp_mixer; 572825eaba2fSLydia Wang 572925eaba2fSLydia Wang return 1; 573025eaba2fSLydia Wang } 573125eaba2fSLydia Wang 573225eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 573325eaba2fSLydia Wang static struct hda_amp_list vt2002P_loopbacks[] = { 573425eaba2fSLydia Wang { 0x21, HDA_INPUT, 0 }, 573525eaba2fSLydia Wang { 0x21, HDA_INPUT, 1 }, 573625eaba2fSLydia Wang { 0x21, HDA_INPUT, 2 }, 573725eaba2fSLydia Wang { } /* end */ 573825eaba2fSLydia Wang }; 573925eaba2fSLydia Wang #endif 574025eaba2fSLydia Wang 574125eaba2fSLydia Wang 574225eaba2fSLydia Wang /* patch for vt2002P */ 574325eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 574425eaba2fSLydia Wang { 574525eaba2fSLydia Wang struct via_spec *spec; 574625eaba2fSLydia Wang int err; 574725eaba2fSLydia Wang 574825eaba2fSLydia Wang /* create a codec specific record */ 574925eaba2fSLydia Wang spec = kzalloc(sizeof(*spec), GFP_KERNEL); 575025eaba2fSLydia Wang if (spec == NULL) 575125eaba2fSLydia Wang return -ENOMEM; 575225eaba2fSLydia Wang 575325eaba2fSLydia Wang codec->spec = spec; 575425eaba2fSLydia Wang 575525eaba2fSLydia Wang /* automatic parse from the BIOS config */ 575625eaba2fSLydia Wang err = vt2002P_parse_auto_config(codec); 575725eaba2fSLydia Wang if (err < 0) { 575825eaba2fSLydia Wang via_free(codec); 575925eaba2fSLydia Wang return err; 576025eaba2fSLydia Wang } else if (!err) { 576125eaba2fSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 576225eaba2fSLydia Wang "from BIOS. Using genenic mode...\n"); 576325eaba2fSLydia Wang } 576425eaba2fSLydia Wang 576525eaba2fSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt2002P_volume_init_verbs; 576625eaba2fSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs; 576725eaba2fSLydia Wang 576825eaba2fSLydia Wang spec->stream_name_analog = "VT2002P Analog"; 576925eaba2fSLydia Wang spec->stream_analog_playback = &vt2002P_pcm_analog_playback; 577025eaba2fSLydia Wang spec->stream_analog_capture = &vt2002P_pcm_analog_capture; 577125eaba2fSLydia Wang 577225eaba2fSLydia Wang spec->stream_name_digital = "VT2002P Digital"; 577325eaba2fSLydia Wang spec->stream_digital_playback = &vt2002P_pcm_digital_playback; 577425eaba2fSLydia Wang 577525eaba2fSLydia Wang if (!spec->adc_nids && spec->input_mux) { 577625eaba2fSLydia Wang spec->adc_nids = vt2002P_adc_nids; 577725eaba2fSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids); 577825eaba2fSLydia Wang get_mux_nids(codec); 577925eaba2fSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 578025eaba2fSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 578125eaba2fSLydia Wang spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; 578225eaba2fSLydia Wang spec->num_mixers++; 578325eaba2fSLydia Wang } 578425eaba2fSLydia Wang 578525eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 578625eaba2fSLydia Wang 578725eaba2fSLydia Wang codec->patch_ops.init = via_auto_init; 578825eaba2fSLydia Wang codec->patch_ops.unsol_event = via_unsol_event, 578925eaba2fSLydia Wang 579025eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 579125eaba2fSLydia Wang spec->loopback.amplist = vt2002P_loopbacks; 579225eaba2fSLydia Wang #endif 579325eaba2fSLydia Wang 579425eaba2fSLydia Wang return 0; 579525eaba2fSLydia Wang } 5796ab6734e7SLydia Wang 5797ab6734e7SLydia Wang /* for vt1812 */ 5798ab6734e7SLydia Wang 5799ab6734e7SLydia Wang /* capture mixer elements */ 5800ab6734e7SLydia Wang static struct snd_kcontrol_new vt1812_capture_mixer[] = { 5801ab6734e7SLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 5802ab6734e7SLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 5803ab6734e7SLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 5804ab6734e7SLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 5805ab6734e7SLydia Wang HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 5806ab6734e7SLydia Wang HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, 5807ab6734e7SLydia Wang HDA_INPUT), 5808ab6734e7SLydia Wang { 5809ab6734e7SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5810ab6734e7SLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 5811ab6734e7SLydia Wang * So call somewhat different.. 5812ab6734e7SLydia Wang */ 5813ab6734e7SLydia Wang .name = "Input Source", 5814ab6734e7SLydia Wang .count = 2, 5815ab6734e7SLydia Wang .info = via_mux_enum_info, 5816ab6734e7SLydia Wang .get = via_mux_enum_get, 5817ab6734e7SLydia Wang .put = via_mux_enum_put, 5818ab6734e7SLydia Wang }, 5819ab6734e7SLydia Wang { } /* end */ 5820ab6734e7SLydia Wang }; 5821ab6734e7SLydia Wang 5822ab6734e7SLydia Wang static struct hda_verb vt1812_volume_init_verbs[] = { 5823ab6734e7SLydia Wang /* 5824ab6734e7SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 5825ab6734e7SLydia Wang */ 5826ab6734e7SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5827ab6734e7SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5828ab6734e7SLydia Wang 5829ab6734e7SLydia Wang 5830ab6734e7SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 5831ab6734e7SLydia Wang * mixer widget 5832ab6734e7SLydia Wang */ 5833ab6734e7SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 5834ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 5835ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 5836ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 5837ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 5838ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 5839ab6734e7SLydia Wang 5840ab6734e7SLydia Wang /* MUX Indices: Mic = 0 */ 5841ab6734e7SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 5842ab6734e7SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 5843ab6734e7SLydia Wang 5844ab6734e7SLydia Wang /* PW9 Output enable */ 5845ab6734e7SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 5846ab6734e7SLydia Wang 5847ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 5848ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 5849ab6734e7SLydia Wang 5850ab6734e7SLydia Wang /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 5851ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5852ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5853ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5854ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5855ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5856ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5857ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5858ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5859ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5860ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5861ab6734e7SLydia Wang 5862ab6734e7SLydia Wang /* set MUX0/1/4/13/15 = 0 (AOW0) */ 5863ab6734e7SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 5864ab6734e7SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 5865ab6734e7SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 5866ab6734e7SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 5867ab6734e7SLydia Wang {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, 5868ab6734e7SLydia Wang 5869ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 5870ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 5871ab6734e7SLydia Wang { } 5872ab6734e7SLydia Wang }; 5873ab6734e7SLydia Wang 5874ab6734e7SLydia Wang 5875ab6734e7SLydia Wang static struct hda_verb vt1812_uniwill_init_verbs[] = { 5876ab6734e7SLydia Wang {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, 5877ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5878ab6734e7SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, 5879ab6734e7SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 5880ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5881ab6734e7SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5882ab6734e7SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5883ab6734e7SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5884ab6734e7SLydia Wang { } 5885ab6734e7SLydia Wang }; 5886ab6734e7SLydia Wang 5887ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_playback = { 5888ab6734e7SLydia Wang .substreams = 2, 5889ab6734e7SLydia Wang .channels_min = 2, 5890ab6734e7SLydia Wang .channels_max = 2, 5891ab6734e7SLydia Wang .nid = 0x8, /* NID to query formats and rates */ 5892ab6734e7SLydia Wang .ops = { 5893ab6734e7SLydia Wang .open = via_playback_pcm_open, 5894ab6734e7SLydia Wang .prepare = via_playback_multi_pcm_prepare, 5895ab6734e7SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 5896ab6734e7SLydia Wang .close = via_pcm_open_close, 5897ab6734e7SLydia Wang }, 5898ab6734e7SLydia Wang }; 5899ab6734e7SLydia Wang 5900ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_capture = { 5901ab6734e7SLydia Wang .substreams = 2, 5902ab6734e7SLydia Wang .channels_min = 2, 5903ab6734e7SLydia Wang .channels_max = 2, 5904ab6734e7SLydia Wang .nid = 0x10, /* NID to query formats and rates */ 5905ab6734e7SLydia Wang .ops = { 5906ab6734e7SLydia Wang .open = via_pcm_open_close, 5907ab6734e7SLydia Wang .prepare = via_capture_pcm_prepare, 5908ab6734e7SLydia Wang .cleanup = via_capture_pcm_cleanup, 5909ab6734e7SLydia Wang .close = via_pcm_open_close, 5910ab6734e7SLydia Wang }, 5911ab6734e7SLydia Wang }; 5912ab6734e7SLydia Wang 5913ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_digital_playback = { 5914ab6734e7SLydia Wang .substreams = 1, 5915ab6734e7SLydia Wang .channels_min = 2, 5916ab6734e7SLydia Wang .channels_max = 2, 5917ab6734e7SLydia Wang .rates = SNDRV_PCM_RATE_48000, 5918ab6734e7SLydia Wang /* NID is set in via_build_pcms */ 5919ab6734e7SLydia Wang .ops = { 5920ab6734e7SLydia Wang .open = via_dig_playback_pcm_open, 5921ab6734e7SLydia Wang .close = via_dig_playback_pcm_close, 5922ab6734e7SLydia Wang .prepare = via_dig_playback_pcm_prepare, 5923ab6734e7SLydia Wang .cleanup = via_dig_playback_pcm_cleanup 5924ab6734e7SLydia Wang }, 5925ab6734e7SLydia Wang }; 5926ab6734e7SLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 5927ab6734e7SLydia Wang static int vt1812_auto_fill_dac_nids(struct via_spec *spec, 5928ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5929ab6734e7SLydia Wang { 5930ab6734e7SLydia Wang spec->multiout.num_dacs = 1; 5931ab6734e7SLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 5932ab6734e7SLydia Wang if (cfg->line_out_pins[0]) 5933ab6734e7SLydia Wang spec->multiout.dac_nids[0] = 0x8; 5934ab6734e7SLydia Wang return 0; 5935ab6734e7SLydia Wang } 5936ab6734e7SLydia Wang 5937ab6734e7SLydia Wang 5938ab6734e7SLydia Wang /* add playback controls from the parsed DAC table */ 5939ab6734e7SLydia Wang static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec, 5940ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5941ab6734e7SLydia Wang { 5942ab6734e7SLydia Wang int err; 5943ab6734e7SLydia Wang 5944ab6734e7SLydia Wang if (!cfg->line_out_pins[0]) 5945ab6734e7SLydia Wang return -1; 5946ab6734e7SLydia Wang 5947ab6734e7SLydia Wang /* Line-Out: PortE */ 5948ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 5949ab6734e7SLydia Wang "Master Front Playback Volume", 5950ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); 5951ab6734e7SLydia Wang if (err < 0) 5952ab6734e7SLydia Wang return err; 5953ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, 5954ab6734e7SLydia Wang "Master Front Playback Switch", 5955ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT)); 5956ab6734e7SLydia Wang if (err < 0) 5957ab6734e7SLydia Wang return err; 5958ab6734e7SLydia Wang 5959ab6734e7SLydia Wang return 0; 5960ab6734e7SLydia Wang } 5961ab6734e7SLydia Wang 5962ab6734e7SLydia Wang static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 5963ab6734e7SLydia Wang { 5964ab6734e7SLydia Wang int err; 5965ab6734e7SLydia Wang 5966ab6734e7SLydia Wang if (!pin) 5967ab6734e7SLydia Wang return 0; 5968ab6734e7SLydia Wang 5969ab6734e7SLydia Wang spec->multiout.hp_nid = 0x9; 5970ab6734e7SLydia Wang spec->hp_independent_mode_index = 1; 5971ab6734e7SLydia Wang 5972ab6734e7SLydia Wang 5973ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 5974ab6734e7SLydia Wang "Headphone Playback Volume", 5975ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL( 5976ab6734e7SLydia Wang spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); 5977ab6734e7SLydia Wang if (err < 0) 5978ab6734e7SLydia Wang return err; 5979ab6734e7SLydia Wang 5980ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 5981ab6734e7SLydia Wang "Headphone Playback Switch", 5982ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 5983ab6734e7SLydia Wang if (err < 0) 5984ab6734e7SLydia Wang return err; 5985ab6734e7SLydia Wang 5986ab6734e7SLydia Wang create_hp_imux(spec); 5987ab6734e7SLydia Wang return 0; 5988ab6734e7SLydia Wang } 5989ab6734e7SLydia Wang 5990ab6734e7SLydia Wang /* create playback/capture controls for input pins */ 5991ab6734e7SLydia Wang static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec, 5992ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5993ab6734e7SLydia Wang { 5994ab6734e7SLydia Wang static char *labels[] = { 5995ab6734e7SLydia Wang "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 5996ab6734e7SLydia Wang }; 5997ab6734e7SLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 5998ab6734e7SLydia Wang int i, err, idx = 0; 5999ab6734e7SLydia Wang 6000ab6734e7SLydia Wang for (i = 0; i < AUTO_PIN_LAST; i++) { 6001ab6734e7SLydia Wang if (!cfg->input_pins[i]) 6002ab6734e7SLydia Wang continue; 6003ab6734e7SLydia Wang 6004ab6734e7SLydia Wang switch (cfg->input_pins[i]) { 6005ab6734e7SLydia Wang case 0x2b: /* Mic */ 6006ab6734e7SLydia Wang idx = 0; 6007ab6734e7SLydia Wang break; 6008ab6734e7SLydia Wang 6009ab6734e7SLydia Wang case 0x2a: /* Line In */ 6010ab6734e7SLydia Wang idx = 1; 6011ab6734e7SLydia Wang break; 6012ab6734e7SLydia Wang 6013ab6734e7SLydia Wang case 0x29: /* Front Mic */ 6014ab6734e7SLydia Wang idx = 2; 6015ab6734e7SLydia Wang break; 6016ab6734e7SLydia Wang } 6017ab6734e7SLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x21); 6018ab6734e7SLydia Wang if (err < 0) 6019ab6734e7SLydia Wang return err; 6020ab6734e7SLydia Wang imux->items[imux->num_items].label = labels[i]; 6021ab6734e7SLydia Wang imux->items[imux->num_items].index = idx; 6022ab6734e7SLydia Wang imux->num_items++; 6023ab6734e7SLydia Wang } 6024ab6734e7SLydia Wang /* build volume/mute control of loopback */ 6025ab6734e7SLydia Wang err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21); 6026ab6734e7SLydia Wang if (err < 0) 6027ab6734e7SLydia Wang return err; 6028ab6734e7SLydia Wang 6029ab6734e7SLydia Wang /* for internal loopback recording select */ 6030ab6734e7SLydia Wang imux->items[imux->num_items].label = "Stereo Mixer"; 6031ab6734e7SLydia Wang imux->items[imux->num_items].index = 5; 6032ab6734e7SLydia Wang imux->num_items++; 6033ab6734e7SLydia Wang 6034ab6734e7SLydia Wang /* for digital mic select */ 6035ab6734e7SLydia Wang imux->items[imux->num_items].label = "Digital Mic"; 6036ab6734e7SLydia Wang imux->items[imux->num_items].index = 6; 6037ab6734e7SLydia Wang imux->num_items++; 6038ab6734e7SLydia Wang 6039ab6734e7SLydia Wang return 0; 6040ab6734e7SLydia Wang } 6041ab6734e7SLydia Wang 6042ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec) 6043ab6734e7SLydia Wang { 6044ab6734e7SLydia Wang struct via_spec *spec = codec->spec; 6045ab6734e7SLydia Wang int err; 6046ab6734e7SLydia Wang 6047ab6734e7SLydia Wang 6048ab6734e7SLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 6049ab6734e7SLydia Wang if (err < 0) 6050ab6734e7SLydia Wang return err; 6051ab6734e7SLydia Wang fill_dig_outs(codec); 6052ab6734e7SLydia Wang err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg); 6053ab6734e7SLydia Wang if (err < 0) 6054ab6734e7SLydia Wang return err; 6055ab6734e7SLydia Wang 6056ab6734e7SLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) 6057ab6734e7SLydia Wang return 0; /* can't find valid BIOS pin config */ 6058ab6734e7SLydia Wang 6059ab6734e7SLydia Wang err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg); 6060ab6734e7SLydia Wang if (err < 0) 6061ab6734e7SLydia Wang return err; 6062ab6734e7SLydia Wang err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 6063ab6734e7SLydia Wang if (err < 0) 6064ab6734e7SLydia Wang return err; 6065ab6734e7SLydia Wang err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg); 6066ab6734e7SLydia Wang if (err < 0) 6067ab6734e7SLydia Wang return err; 6068ab6734e7SLydia Wang 6069ab6734e7SLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 6070ab6734e7SLydia Wang 6071ab6734e7SLydia Wang fill_dig_outs(codec); 6072ab6734e7SLydia Wang 6073ab6734e7SLydia Wang if (spec->kctls.list) 6074ab6734e7SLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 6075ab6734e7SLydia Wang 6076ab6734e7SLydia Wang spec->input_mux = &spec->private_imux[0]; 6077ab6734e7SLydia Wang 6078ab6734e7SLydia Wang if (spec->hp_mux) 6079ab6734e7SLydia Wang spec->mixers[spec->num_mixers++] = via_hp_mixer; 6080ab6734e7SLydia Wang 6081ab6734e7SLydia Wang return 1; 6082ab6734e7SLydia Wang } 6083ab6734e7SLydia Wang 6084ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 6085ab6734e7SLydia Wang static struct hda_amp_list vt1812_loopbacks[] = { 6086ab6734e7SLydia Wang { 0x21, HDA_INPUT, 0 }, 6087ab6734e7SLydia Wang { 0x21, HDA_INPUT, 1 }, 6088ab6734e7SLydia Wang { 0x21, HDA_INPUT, 2 }, 6089ab6734e7SLydia Wang { } /* end */ 6090ab6734e7SLydia Wang }; 6091ab6734e7SLydia Wang #endif 6092ab6734e7SLydia Wang 6093ab6734e7SLydia Wang 6094ab6734e7SLydia Wang /* patch for vt1812 */ 6095ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 6096ab6734e7SLydia Wang { 6097ab6734e7SLydia Wang struct via_spec *spec; 6098ab6734e7SLydia Wang int err; 6099ab6734e7SLydia Wang 6100ab6734e7SLydia Wang /* create a codec specific record */ 6101ab6734e7SLydia Wang spec = kzalloc(sizeof(*spec), GFP_KERNEL); 6102ab6734e7SLydia Wang if (spec == NULL) 6103ab6734e7SLydia Wang return -ENOMEM; 6104ab6734e7SLydia Wang 6105ab6734e7SLydia Wang codec->spec = spec; 6106ab6734e7SLydia Wang 6107ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 6108ab6734e7SLydia Wang err = vt1812_parse_auto_config(codec); 6109ab6734e7SLydia Wang if (err < 0) { 6110ab6734e7SLydia Wang via_free(codec); 6111ab6734e7SLydia Wang return err; 6112ab6734e7SLydia Wang } else if (!err) { 6113ab6734e7SLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 6114ab6734e7SLydia Wang "from BIOS. Using genenic mode...\n"); 6115ab6734e7SLydia Wang } 6116ab6734e7SLydia Wang 6117ab6734e7SLydia Wang 6118ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; 6119ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; 6120ab6734e7SLydia Wang 6121ab6734e7SLydia Wang spec->stream_name_analog = "VT1812 Analog"; 6122ab6734e7SLydia Wang spec->stream_analog_playback = &vt1812_pcm_analog_playback; 6123ab6734e7SLydia Wang spec->stream_analog_capture = &vt1812_pcm_analog_capture; 6124ab6734e7SLydia Wang 6125ab6734e7SLydia Wang spec->stream_name_digital = "VT1812 Digital"; 6126ab6734e7SLydia Wang spec->stream_digital_playback = &vt1812_pcm_digital_playback; 6127ab6734e7SLydia Wang 6128ab6734e7SLydia Wang 6129ab6734e7SLydia Wang if (!spec->adc_nids && spec->input_mux) { 6130ab6734e7SLydia Wang spec->adc_nids = vt1812_adc_nids; 6131ab6734e7SLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids); 6132ab6734e7SLydia Wang get_mux_nids(codec); 6133ab6734e7SLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 6134ab6734e7SLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 6135ab6734e7SLydia Wang spec->mixers[spec->num_mixers] = vt1812_capture_mixer; 6136ab6734e7SLydia Wang spec->num_mixers++; 6137ab6734e7SLydia Wang } 6138ab6734e7SLydia Wang 6139ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 6140ab6734e7SLydia Wang 6141ab6734e7SLydia Wang codec->patch_ops.init = via_auto_init; 6142ab6734e7SLydia Wang codec->patch_ops.unsol_event = via_unsol_event, 6143ab6734e7SLydia Wang 6144ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 6145ab6734e7SLydia Wang spec->loopback.amplist = vt1812_loopbacks; 6146ab6734e7SLydia Wang #endif 6147ab6734e7SLydia Wang 6148ab6734e7SLydia Wang return 0; 6149ab6734e7SLydia Wang } 6150ab6734e7SLydia Wang 6151c577b8a1SJoseph Chan /* 6152c577b8a1SJoseph Chan * patch entries 6153c577b8a1SJoseph Chan */ 61541289e9e8STakashi Iwai static struct hda_codec_preset snd_hda_preset_via[] = { 61553218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 61563218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 61573218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 61583218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 61593218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 6160f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 61613218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 6162f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 61633218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 6164f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 61653218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 6166f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 61673218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 6168f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 61693218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 6170f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 61713218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 6172f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 61733218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 6174f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 61753218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 6176f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 61773218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 6178f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 61793218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 6180f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 61813218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 6182f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 61833218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 6184f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 61853218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 6186f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 61873218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 6188f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 61893218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 6190f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 61913218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 6192d949cac1SHarald Welte .patch = patch_vt1708S}, 61933218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 6194d949cac1SHarald Welte .patch = patch_vt1708S}, 61953218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 6196d949cac1SHarald Welte .patch = patch_vt1708S}, 61973218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 6198d949cac1SHarald Welte .patch = patch_vt1708S}, 61993218c178STakashi Iwai { .id = 0x11064397, .name = "VT1708S", 6200d949cac1SHarald Welte .patch = patch_vt1708S}, 62013218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 6202d949cac1SHarald Welte .patch = patch_vt1708S}, 62033218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 6204d949cac1SHarald Welte .patch = patch_vt1708S}, 62053218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 6206d949cac1SHarald Welte .patch = patch_vt1708S}, 62073218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 6208d949cac1SHarald Welte .patch = patch_vt1702}, 62093218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 6210d949cac1SHarald Welte .patch = patch_vt1702}, 62113218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 6212d949cac1SHarald Welte .patch = patch_vt1702}, 62133218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 6214d949cac1SHarald Welte .patch = patch_vt1702}, 62153218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 6216d949cac1SHarald Welte .patch = patch_vt1702}, 62173218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 6218d949cac1SHarald Welte .patch = patch_vt1702}, 62193218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 6220d949cac1SHarald Welte .patch = patch_vt1702}, 62213218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 6222d949cac1SHarald Welte .patch = patch_vt1702}, 6223eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 6224eb7188caSLydia Wang .patch = patch_vt1718S}, 6225eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 6226eb7188caSLydia Wang .patch = patch_vt1718S}, 6227bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 6228bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 6229bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 6230bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 6231f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 6232f3db423dSLydia Wang .patch = patch_vt1716S}, 6233f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 6234f3db423dSLydia Wang .patch = patch_vt1716S}, 623525eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 623625eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 6237ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 6238c577b8a1SJoseph Chan {} /* terminator */ 6239c577b8a1SJoseph Chan }; 62401289e9e8STakashi Iwai 62411289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 62421289e9e8STakashi Iwai 62431289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 62441289e9e8STakashi Iwai .preset = snd_hda_preset_via, 62451289e9e8STakashi Iwai .owner = THIS_MODULE, 62461289e9e8STakashi Iwai }; 62471289e9e8STakashi Iwai 62481289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 62491289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 62501289e9e8STakashi Iwai 62511289e9e8STakashi Iwai static int __init patch_via_init(void) 62521289e9e8STakashi Iwai { 62531289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 62541289e9e8STakashi Iwai } 62551289e9e8STakashi Iwai 62561289e9e8STakashi Iwai static void __exit patch_via_exit(void) 62571289e9e8STakashi Iwai { 62581289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 62591289e9e8STakashi Iwai } 62601289e9e8STakashi Iwai 62611289e9e8STakashi Iwai module_init(patch_via_init) 62621289e9e8STakashi Iwai module_exit(patch_via_exit) 6263