1c577b8a1SJoseph Chan /* 2c577b8a1SJoseph Chan * Universal Interface for Intel High Definition Audio Codec 3c577b8a1SJoseph Chan * 4d949cac1SHarald Welte * HD audio interface patch for VIA VT1702/VT1708/VT1709 codec 5c577b8a1SJoseph Chan * 676d9b0ddSHarald Welte * Copyright (c) 2006-2008 Lydia Wang <lydiawang@viatech.com> 7c577b8a1SJoseph Chan * 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 */ 39c577b8a1SJoseph Chan /* */ 40c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 41c577b8a1SJoseph Chan 42c577b8a1SJoseph Chan 43c577b8a1SJoseph Chan #include <linux/init.h> 44c577b8a1SJoseph Chan #include <linux/delay.h> 45c577b8a1SJoseph Chan #include <linux/slab.h> 46c577b8a1SJoseph Chan #include <sound/core.h> 470aa62aefSHarald Welte #include <sound/asoundef.h> 48c577b8a1SJoseph Chan #include "hda_codec.h" 49c577b8a1SJoseph Chan #include "hda_local.h" 50c577b8a1SJoseph Chan 51c577b8a1SJoseph Chan /* amp values */ 52c577b8a1SJoseph Chan #define AMP_VAL_IDX_SHIFT 19 53c577b8a1SJoseph Chan #define AMP_VAL_IDX_MASK (0x0f<<19) 54c577b8a1SJoseph Chan 55c577b8a1SJoseph Chan /* Pin Widget NID */ 56c577b8a1SJoseph Chan #define VT1708_HP_NID 0x13 57c577b8a1SJoseph Chan #define VT1708_DIGOUT_NID 0x14 58c577b8a1SJoseph Chan #define VT1708_DIGIN_NID 0x16 59f7278fd0SJosepch Chan #define VT1708_DIGIN_PIN 0x26 6076d9b0ddSHarald Welte #define VT1708_HP_PIN_NID 0x20 6176d9b0ddSHarald Welte #define VT1708_CD_PIN_NID 0x24 62c577b8a1SJoseph Chan 63c577b8a1SJoseph Chan #define VT1709_HP_DAC_NID 0x28 64c577b8a1SJoseph Chan #define VT1709_DIGOUT_NID 0x13 65c577b8a1SJoseph Chan #define VT1709_DIGIN_NID 0x17 66f7278fd0SJosepch Chan #define VT1709_DIGIN_PIN 0x25 67f7278fd0SJosepch Chan 68f7278fd0SJosepch Chan #define VT1708B_HP_NID 0x25 69f7278fd0SJosepch Chan #define VT1708B_DIGOUT_NID 0x12 70f7278fd0SJosepch Chan #define VT1708B_DIGIN_NID 0x15 71f7278fd0SJosepch Chan #define VT1708B_DIGIN_PIN 0x21 72c577b8a1SJoseph Chan 73d949cac1SHarald Welte #define VT1708S_HP_NID 0x25 74d949cac1SHarald Welte #define VT1708S_DIGOUT_NID 0x12 75d949cac1SHarald Welte 76d949cac1SHarald Welte #define VT1702_HP_NID 0x17 77d949cac1SHarald Welte #define VT1702_DIGOUT_NID 0x11 78d949cac1SHarald Welte 79d7426329SHarald Welte enum VIA_HDA_CODEC { 80d7426329SHarald Welte UNKNOWN = -1, 81d7426329SHarald Welte VT1708, 82d7426329SHarald Welte VT1709_10CH, 83d7426329SHarald Welte VT1709_6CH, 84d7426329SHarald Welte VT1708B_8CH, 85d7426329SHarald Welte VT1708B_4CH, 86d7426329SHarald Welte VT1708S, 87518bf3baSLydia Wang VT1708BCE, 88d7426329SHarald Welte VT1702, 89eb7188caSLydia Wang VT1718S, 90f3db423dSLydia Wang VT1716S, 91*25eaba2fSLydia Wang VT2002P, 92d7426329SHarald Welte CODEC_TYPES, 93d7426329SHarald Welte }; 94d7426329SHarald Welte 951f2e99feSLydia Wang struct via_spec { 961f2e99feSLydia Wang /* codec parameterization */ 97f3db423dSLydia Wang struct snd_kcontrol_new *mixers[6]; 981f2e99feSLydia Wang unsigned int num_mixers; 991f2e99feSLydia Wang 1001f2e99feSLydia Wang struct hda_verb *init_verbs[5]; 1011f2e99feSLydia Wang unsigned int num_iverbs; 1021f2e99feSLydia Wang 1031f2e99feSLydia Wang char *stream_name_analog; 1041f2e99feSLydia Wang struct hda_pcm_stream *stream_analog_playback; 1051f2e99feSLydia Wang struct hda_pcm_stream *stream_analog_capture; 1061f2e99feSLydia Wang 1071f2e99feSLydia Wang char *stream_name_digital; 1081f2e99feSLydia Wang struct hda_pcm_stream *stream_digital_playback; 1091f2e99feSLydia Wang struct hda_pcm_stream *stream_digital_capture; 1101f2e99feSLydia Wang 1111f2e99feSLydia Wang /* playback */ 1121f2e99feSLydia Wang struct hda_multi_out multiout; 1131f2e99feSLydia Wang hda_nid_t slave_dig_outs[2]; 1141f2e99feSLydia Wang 1151f2e99feSLydia Wang /* capture */ 1161f2e99feSLydia Wang unsigned int num_adc_nids; 1171f2e99feSLydia Wang hda_nid_t *adc_nids; 1181f2e99feSLydia Wang hda_nid_t mux_nids[3]; 1191f2e99feSLydia Wang hda_nid_t dig_in_nid; 1201f2e99feSLydia Wang hda_nid_t dig_in_pin; 1211f2e99feSLydia Wang 1221f2e99feSLydia Wang /* capture source */ 1231f2e99feSLydia Wang const struct hda_input_mux *input_mux; 1241f2e99feSLydia Wang unsigned int cur_mux[3]; 1251f2e99feSLydia Wang 1261f2e99feSLydia Wang /* PCM information */ 1271f2e99feSLydia Wang struct hda_pcm pcm_rec[3]; 1281f2e99feSLydia Wang 1291f2e99feSLydia Wang /* dynamic controls, init_verbs and input_mux */ 1301f2e99feSLydia Wang struct auto_pin_cfg autocfg; 1311f2e99feSLydia Wang struct snd_array kctls; 1321f2e99feSLydia Wang struct hda_input_mux private_imux[2]; 1331f2e99feSLydia Wang hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 1341f2e99feSLydia Wang 1351f2e99feSLydia Wang /* HP mode source */ 1361f2e99feSLydia Wang const struct hda_input_mux *hp_mux; 1371f2e99feSLydia Wang unsigned int hp_independent_mode; 1381f2e99feSLydia Wang unsigned int hp_independent_mode_index; 1391f2e99feSLydia Wang unsigned int smart51_enabled; 140f3db423dSLydia Wang unsigned int dmic_enabled; 1411f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1421f2e99feSLydia Wang 1431f2e99feSLydia Wang /* work to check hp jack state */ 1441f2e99feSLydia Wang struct hda_codec *codec; 1451f2e99feSLydia Wang struct delayed_work vt1708_hp_work; 1461f2e99feSLydia Wang int vt1708_jack_detectect; 1471f2e99feSLydia Wang int vt1708_hp_present; 1481f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 1491f2e99feSLydia Wang struct hda_loopback_check loopback; 1501f2e99feSLydia Wang #endif 1511f2e99feSLydia Wang }; 1521f2e99feSLydia Wang 153744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 154d7426329SHarald Welte { 155744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 156d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 157d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 158d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 159d7426329SHarald Welte 160d7426329SHarald Welte /* get codec type */ 161d7426329SHarald Welte if (ven_id != 0x1106) 162d7426329SHarald Welte codec_type = UNKNOWN; 163d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 164d7426329SHarald Welte codec_type = VT1708; 165d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 166d7426329SHarald Welte codec_type = VT1709_10CH; 167d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 168d7426329SHarald Welte codec_type = VT1709_6CH; 169518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 170d7426329SHarald Welte codec_type = VT1708B_8CH; 171518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 172518bf3baSLydia Wang codec_type = VT1708BCE; 173518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 174d7426329SHarald Welte codec_type = VT1708B_4CH; 175d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 176d7426329SHarald Welte && (dev_id >> 12) < 8) 177d7426329SHarald Welte codec_type = VT1708S; 178d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 179d7426329SHarald Welte && (dev_id >> 12) < 8) 180d7426329SHarald Welte codec_type = VT1702; 181eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 182eb7188caSLydia Wang && (dev_id >> 12) < 8) 183eb7188caSLydia Wang codec_type = VT1718S; 184f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 185f3db423dSLydia Wang codec_type = VT1716S; 186bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 187bb3c6bfcSLydia Wang codec_type = VT1718S; 188*25eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 189*25eaba2fSLydia Wang codec_type = VT2002P; 190d7426329SHarald Welte else 191d7426329SHarald Welte codec_type = UNKNOWN; 192d7426329SHarald Welte return codec_type; 193d7426329SHarald Welte }; 194d7426329SHarald Welte 19569e52a80SHarald Welte #define VIA_HP_EVENT 0x01 19669e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 197a34df19aSLydia Wang #define VIA_JACK_EVENT 0x04 198f3db423dSLydia Wang #define VIA_MONO_EVENT 0x08 199*25eaba2fSLydia Wang #define VIA_SPEAKER_EVENT 0x10 200*25eaba2fSLydia Wang #define VIA_BIND_HP_EVENT 0x20 20169e52a80SHarald Welte 202c577b8a1SJoseph Chan enum { 203c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 204c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 205f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 206*25eaba2fSLydia Wang VIA_CTL_WIDGET_BIND_PIN_MUTE, 207c577b8a1SJoseph Chan }; 208c577b8a1SJoseph Chan 209c577b8a1SJoseph Chan enum { 210eb14a46cSHarald Welte AUTO_SEQ_FRONT = 0, 211c577b8a1SJoseph Chan AUTO_SEQ_SURROUND, 212c577b8a1SJoseph Chan AUTO_SEQ_CENLFE, 213c577b8a1SJoseph Chan AUTO_SEQ_SIDE 214c577b8a1SJoseph Chan }; 215c577b8a1SJoseph Chan 216f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); 217f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec); 2181f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec); 2191f2e99feSLydia Wang 2201f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec) 2211f2e99feSLydia Wang { 2221f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2231f2e99feSLydia Wang return; 2241f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 2251f2e99feSLydia Wang !spec->vt1708_jack_detectect); 2261f2e99feSLydia Wang if (!delayed_work_pending(&spec->vt1708_hp_work)) 2271f2e99feSLydia Wang schedule_delayed_work(&spec->vt1708_hp_work, 2281f2e99feSLydia Wang msecs_to_jiffies(100)); 2291f2e99feSLydia Wang } 2301f2e99feSLydia Wang 2311f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec) 2321f2e99feSLydia Wang { 2331f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2341f2e99feSLydia Wang return; 2351f2e99feSLydia Wang if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 2361f2e99feSLydia Wang && !is_aa_path_mute(spec->codec)) 2371f2e99feSLydia Wang return; 2381f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 2391f2e99feSLydia Wang !spec->vt1708_jack_detectect); 2401f2e99feSLydia Wang cancel_delayed_work(&spec->vt1708_hp_work); 2411f2e99feSLydia Wang flush_scheduled_work(); 2421f2e99feSLydia Wang } 243f5271101SLydia Wang 244*25eaba2fSLydia Wang 245f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 246f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 247f5271101SLydia Wang { 248f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 249f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 250f5271101SLydia Wang 251f5271101SLydia Wang set_jack_power_state(codec); 252f5271101SLydia Wang analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); 2531f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 2541f2e99feSLydia Wang if (is_aa_path_mute(codec)) 2551f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 2561f2e99feSLydia Wang else 2571f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 2581f2e99feSLydia Wang } 259f5271101SLydia Wang return change; 260f5271101SLydia Wang } 261f5271101SLydia Wang 262f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 263f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 264f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 265f5271101SLydia Wang .name = NULL, \ 266f5271101SLydia Wang .index = 0, \ 267f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 268f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 269f5271101SLydia Wang .put = analog_input_switch_put, \ 270f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 271f5271101SLydia Wang 272*25eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec); 273*25eaba2fSLydia Wang 274*25eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, 275*25eaba2fSLydia Wang struct snd_ctl_elem_value *ucontrol) 276*25eaba2fSLydia Wang { 277*25eaba2fSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 278*25eaba2fSLydia Wang struct via_spec *spec = codec->spec; 279*25eaba2fSLydia Wang int i; 280*25eaba2fSLydia Wang int change = 0; 281*25eaba2fSLydia Wang 282*25eaba2fSLydia Wang long *valp = ucontrol->value.integer.value; 283*25eaba2fSLydia Wang int lmute, rmute; 284*25eaba2fSLydia Wang if (strstr(kcontrol->id.name, "Switch") == NULL) { 285*25eaba2fSLydia Wang snd_printd("Invalid control!\n"); 286*25eaba2fSLydia Wang return change; 287*25eaba2fSLydia Wang } 288*25eaba2fSLydia Wang change = snd_hda_mixer_amp_switch_put(kcontrol, 289*25eaba2fSLydia Wang ucontrol); 290*25eaba2fSLydia Wang /* Get mute value */ 291*25eaba2fSLydia Wang lmute = *valp ? 0 : HDA_AMP_MUTE; 292*25eaba2fSLydia Wang valp++; 293*25eaba2fSLydia Wang rmute = *valp ? 0 : HDA_AMP_MUTE; 294*25eaba2fSLydia Wang 295*25eaba2fSLydia Wang /* Set hp pins */ 296*25eaba2fSLydia Wang if (!spec->hp_independent_mode) { 297*25eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 298*25eaba2fSLydia Wang snd_hda_codec_amp_update( 299*25eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 300*25eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 301*25eaba2fSLydia Wang lmute); 302*25eaba2fSLydia Wang snd_hda_codec_amp_update( 303*25eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 304*25eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 305*25eaba2fSLydia Wang rmute); 306*25eaba2fSLydia Wang } 307*25eaba2fSLydia Wang } 308*25eaba2fSLydia Wang 309*25eaba2fSLydia Wang if (!lmute && !rmute) { 310*25eaba2fSLydia Wang /* Line Outs */ 311*25eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 312*25eaba2fSLydia Wang snd_hda_codec_amp_stereo( 313*25eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 314*25eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 315*25eaba2fSLydia Wang /* Speakers */ 316*25eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 317*25eaba2fSLydia Wang snd_hda_codec_amp_stereo( 318*25eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], 319*25eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 320*25eaba2fSLydia Wang /* unmute */ 321*25eaba2fSLydia Wang via_hp_bind_automute(codec); 322*25eaba2fSLydia Wang 323*25eaba2fSLydia Wang } else { 324*25eaba2fSLydia Wang if (lmute) { 325*25eaba2fSLydia Wang /* Mute all left channels */ 326*25eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 327*25eaba2fSLydia Wang snd_hda_codec_amp_update( 328*25eaba2fSLydia Wang codec, 329*25eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 330*25eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 331*25eaba2fSLydia Wang lmute); 332*25eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 333*25eaba2fSLydia Wang snd_hda_codec_amp_update( 334*25eaba2fSLydia Wang codec, 335*25eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 336*25eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 337*25eaba2fSLydia Wang lmute); 338*25eaba2fSLydia Wang } 339*25eaba2fSLydia Wang if (rmute) { 340*25eaba2fSLydia Wang /* mute all right channels */ 341*25eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 342*25eaba2fSLydia Wang snd_hda_codec_amp_update( 343*25eaba2fSLydia Wang codec, 344*25eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 345*25eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 346*25eaba2fSLydia Wang rmute); 347*25eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 348*25eaba2fSLydia Wang snd_hda_codec_amp_update( 349*25eaba2fSLydia Wang codec, 350*25eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 351*25eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 352*25eaba2fSLydia Wang rmute); 353*25eaba2fSLydia Wang } 354*25eaba2fSLydia Wang } 355*25eaba2fSLydia Wang return change; 356*25eaba2fSLydia Wang } 357*25eaba2fSLydia Wang 358*25eaba2fSLydia Wang #define BIND_PIN_MUTE \ 359*25eaba2fSLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 360*25eaba2fSLydia Wang .name = NULL, \ 361*25eaba2fSLydia Wang .index = 0, \ 362*25eaba2fSLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 363*25eaba2fSLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 364*25eaba2fSLydia Wang .put = bind_pin_switch_put, \ 365*25eaba2fSLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 366*25eaba2fSLydia Wang 367c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_control_templates[] = { 368c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 369c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 370f5271101SLydia Wang ANALOG_INPUT_MUTE, 371*25eaba2fSLydia Wang BIND_PIN_MUTE, 372c577b8a1SJoseph Chan }; 373c577b8a1SJoseph Chan 374c577b8a1SJoseph Chan static hda_nid_t vt1708_adc_nids[2] = { 375c577b8a1SJoseph Chan /* ADC1-2 */ 376c577b8a1SJoseph Chan 0x15, 0x27 377c577b8a1SJoseph Chan }; 378c577b8a1SJoseph Chan 379c577b8a1SJoseph Chan static hda_nid_t vt1709_adc_nids[3] = { 380c577b8a1SJoseph Chan /* ADC1-2 */ 381c577b8a1SJoseph Chan 0x14, 0x15, 0x16 382c577b8a1SJoseph Chan }; 383c577b8a1SJoseph Chan 384f7278fd0SJosepch Chan static hda_nid_t vt1708B_adc_nids[2] = { 385f7278fd0SJosepch Chan /* ADC1-2 */ 386f7278fd0SJosepch Chan 0x13, 0x14 387f7278fd0SJosepch Chan }; 388f7278fd0SJosepch Chan 389d949cac1SHarald Welte static hda_nid_t vt1708S_adc_nids[2] = { 390d949cac1SHarald Welte /* ADC1-2 */ 391d949cac1SHarald Welte 0x13, 0x14 392d949cac1SHarald Welte }; 393d949cac1SHarald Welte 394d949cac1SHarald Welte static hda_nid_t vt1702_adc_nids[3] = { 395d949cac1SHarald Welte /* ADC1-2 */ 396d949cac1SHarald Welte 0x12, 0x20, 0x1F 397d949cac1SHarald Welte }; 398d949cac1SHarald Welte 399eb7188caSLydia Wang static hda_nid_t vt1718S_adc_nids[2] = { 400eb7188caSLydia Wang /* ADC1-2 */ 401eb7188caSLydia Wang 0x10, 0x11 402eb7188caSLydia Wang }; 403eb7188caSLydia Wang 404f3db423dSLydia Wang static hda_nid_t vt1716S_adc_nids[2] = { 405f3db423dSLydia Wang /* ADC1-2 */ 406f3db423dSLydia Wang 0x13, 0x14 407f3db423dSLydia Wang }; 408f3db423dSLydia Wang 409*25eaba2fSLydia Wang static hda_nid_t vt2002P_adc_nids[2] = { 410*25eaba2fSLydia Wang /* ADC1-2 */ 411*25eaba2fSLydia Wang 0x10, 0x11 412*25eaba2fSLydia Wang }; 413*25eaba2fSLydia Wang 414c577b8a1SJoseph Chan /* add dynamic controls */ 415c577b8a1SJoseph Chan static int via_add_control(struct via_spec *spec, int type, const char *name, 416c577b8a1SJoseph Chan unsigned long val) 417c577b8a1SJoseph Chan { 418c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 419c577b8a1SJoseph Chan 420603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 421603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 422c577b8a1SJoseph Chan if (!knew) 423c577b8a1SJoseph Chan return -ENOMEM; 424c577b8a1SJoseph Chan *knew = vt1708_control_templates[type]; 425c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 426c577b8a1SJoseph Chan if (!knew->name) 427c577b8a1SJoseph Chan return -ENOMEM; 428c577b8a1SJoseph Chan knew->private_value = val; 429c577b8a1SJoseph Chan return 0; 430c577b8a1SJoseph Chan } 431c577b8a1SJoseph Chan 432603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 433603c4019STakashi Iwai { 434603c4019STakashi Iwai struct via_spec *spec = codec->spec; 435603c4019STakashi Iwai 436603c4019STakashi Iwai if (spec->kctls.list) { 437603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 438603c4019STakashi Iwai int i; 439603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 440603c4019STakashi Iwai kfree(kctl[i].name); 441603c4019STakashi Iwai } 442603c4019STakashi Iwai snd_array_free(&spec->kctls); 443603c4019STakashi Iwai } 444603c4019STakashi Iwai 445c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 4469510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 4479510e8ddSLydia Wang int idx, int mix_nid) 448c577b8a1SJoseph Chan { 449c577b8a1SJoseph Chan char name[32]; 450c577b8a1SJoseph Chan int err; 451c577b8a1SJoseph Chan 452c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 453c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 454c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 455c577b8a1SJoseph Chan if (err < 0) 456c577b8a1SJoseph Chan return err; 457c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 458f5271101SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, 459c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 460c577b8a1SJoseph Chan if (err < 0) 461c577b8a1SJoseph Chan return err; 462c577b8a1SJoseph Chan return 0; 463c577b8a1SJoseph Chan } 464c577b8a1SJoseph Chan 465c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec, 466c577b8a1SJoseph Chan hda_nid_t nid, int pin_type, 467c577b8a1SJoseph Chan int dac_idx) 468c577b8a1SJoseph Chan { 469c577b8a1SJoseph Chan /* set as output */ 470c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 471c577b8a1SJoseph Chan pin_type); 472c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 473c577b8a1SJoseph Chan AMP_OUT_UNMUTE); 474d3a11e60STakashi Iwai if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) 475d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 476d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 477c577b8a1SJoseph Chan } 478c577b8a1SJoseph Chan 479c577b8a1SJoseph Chan 480c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 481c577b8a1SJoseph Chan { 482c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 483c577b8a1SJoseph Chan int i; 484c577b8a1SJoseph Chan 485c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 486c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.line_out_pins[i]; 487c577b8a1SJoseph Chan if (nid) 488c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); 489c577b8a1SJoseph Chan } 490c577b8a1SJoseph Chan } 491c577b8a1SJoseph Chan 492c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 493c577b8a1SJoseph Chan { 494c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 495c577b8a1SJoseph Chan hda_nid_t pin; 496*25eaba2fSLydia Wang int i; 497c577b8a1SJoseph Chan 498*25eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 499*25eaba2fSLydia Wang pin = spec->autocfg.hp_pins[i]; 500c577b8a1SJoseph Chan if (pin) /* connect to front */ 501c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); 502c577b8a1SJoseph Chan } 503*25eaba2fSLydia Wang } 504c577b8a1SJoseph Chan 505c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 506c577b8a1SJoseph Chan { 507c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 508c577b8a1SJoseph Chan int i; 509c577b8a1SJoseph Chan 510c577b8a1SJoseph Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 511c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.input_pins[i]; 512c577b8a1SJoseph Chan 513c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 514c577b8a1SJoseph Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 515c577b8a1SJoseph Chan (i <= AUTO_PIN_FRONT_MIC ? 516c577b8a1SJoseph Chan PIN_VREF50 : PIN_IN)); 517c577b8a1SJoseph Chan 518c577b8a1SJoseph Chan } 519c577b8a1SJoseph Chan } 520f5271101SLydia Wang 5211564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); 5221564b287SLydia Wang 523f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 524f5271101SLydia Wang unsigned int *affected_parm) 525f5271101SLydia Wang { 526f5271101SLydia Wang unsigned parm; 527f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 528f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 529f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 530f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 531f5271101SLydia Wang unsigned present = snd_hda_codec_read(codec, nid, 0, 532f5271101SLydia Wang AC_VERB_GET_PIN_SENSE, 0) >> 31; 5331564b287SLydia Wang struct via_spec *spec = codec->spec; 5341564b287SLydia Wang if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) 5351564b287SLydia Wang || ((no_presence || present) 5361564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 537f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 538f5271101SLydia Wang parm = AC_PWRST_D0; 539f5271101SLydia Wang } else 540f5271101SLydia Wang parm = AC_PWRST_D3; 541f5271101SLydia Wang 542f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 543f5271101SLydia Wang } 544f5271101SLydia Wang 545f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec) 546f5271101SLydia Wang { 547f5271101SLydia Wang struct via_spec *spec = codec->spec; 548f5271101SLydia Wang int imux_is_smixer; 549f5271101SLydia Wang unsigned int parm; 550f5271101SLydia Wang 551f5271101SLydia Wang if (spec->codec_type == VT1702) { 552f5271101SLydia Wang imux_is_smixer = snd_hda_codec_read( 553f5271101SLydia Wang codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 554f5271101SLydia Wang /* inputs */ 555f5271101SLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 556f5271101SLydia Wang parm = AC_PWRST_D3; 557f5271101SLydia Wang set_pin_power_state(codec, 0x14, &parm); 558f5271101SLydia Wang set_pin_power_state(codec, 0x15, &parm); 559f5271101SLydia Wang set_pin_power_state(codec, 0x18, &parm); 560f5271101SLydia Wang if (imux_is_smixer) 561f5271101SLydia Wang parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */ 562f5271101SLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 563f5271101SLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, 564f5271101SLydia Wang parm); 565f5271101SLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, 566f5271101SLydia Wang parm); 567f5271101SLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, 568f5271101SLydia Wang parm); 569f5271101SLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, 570f5271101SLydia Wang parm); 571f5271101SLydia Wang 572f5271101SLydia Wang /* outputs */ 573f5271101SLydia Wang /* PW 3/4 (16h/17h) */ 574f5271101SLydia Wang parm = AC_PWRST_D3; 575f5271101SLydia Wang set_pin_power_state(codec, 0x16, &parm); 576f5271101SLydia Wang set_pin_power_state(codec, 0x17, &parm); 577f5271101SLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 578f5271101SLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 579f5271101SLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 580f5271101SLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 581f5271101SLydia Wang parm); 582f5271101SLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, 583f5271101SLydia Wang parm); 584f5271101SLydia Wang } else if (spec->codec_type == VT1708B_8CH 585f5271101SLydia Wang || spec->codec_type == VT1708B_4CH 586f5271101SLydia Wang || spec->codec_type == VT1708S) { 587f5271101SLydia Wang /* SW0 (17h) = stereo mixer */ 588f5271101SLydia Wang int is_8ch = spec->codec_type != VT1708B_4CH; 589f5271101SLydia Wang imux_is_smixer = snd_hda_codec_read( 590f5271101SLydia Wang codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 591f5271101SLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0); 592f5271101SLydia Wang /* inputs */ 593f5271101SLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 594f5271101SLydia Wang parm = AC_PWRST_D3; 595f5271101SLydia Wang set_pin_power_state(codec, 0x1a, &parm); 596f5271101SLydia Wang set_pin_power_state(codec, 0x1b, &parm); 597f5271101SLydia Wang set_pin_power_state(codec, 0x1e, &parm); 598f5271101SLydia Wang if (imux_is_smixer) 599f5271101SLydia Wang parm = AC_PWRST_D0; 600f5271101SLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 601f5271101SLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, 602f5271101SLydia Wang parm); 603f5271101SLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, 604f5271101SLydia Wang parm); 605f5271101SLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, 606f5271101SLydia Wang parm); 607f5271101SLydia Wang 608f5271101SLydia Wang /* outputs */ 609f5271101SLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 610f5271101SLydia Wang parm = AC_PWRST_D3; 611f5271101SLydia Wang set_pin_power_state(codec, 0x19, &parm); 612f5271101SLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, 613f5271101SLydia Wang parm); 614f5271101SLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, 615f5271101SLydia Wang parm); 616f5271101SLydia Wang 617f5271101SLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 618f5271101SLydia Wang if (is_8ch) { 619f5271101SLydia Wang parm = AC_PWRST_D3; 620f5271101SLydia Wang set_pin_power_state(codec, 0x22, &parm); 621f5271101SLydia Wang snd_hda_codec_write(codec, 0x26, 0, 622f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 623f5271101SLydia Wang snd_hda_codec_write(codec, 0x24, 0, 624f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 625f5271101SLydia Wang } 626f5271101SLydia Wang 627f5271101SLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 628f5271101SLydia Wang parm = AC_PWRST_D3; 629f5271101SLydia Wang /* force to D0 for internal Speaker */ 630f5271101SLydia Wang set_pin_power_state(codec, 0x1c, &parm); 631f5271101SLydia Wang set_pin_power_state(codec, 0x1d, &parm); 632f5271101SLydia Wang if (is_8ch) 633f5271101SLydia Wang set_pin_power_state(codec, 0x23, &parm); 634f5271101SLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 635f5271101SLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 636f5271101SLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 637f5271101SLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 638f5271101SLydia Wang parm); 639f5271101SLydia Wang if (is_8ch) { 640f5271101SLydia Wang snd_hda_codec_write(codec, 0x25, 0, 641f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 642f5271101SLydia Wang snd_hda_codec_write(codec, 0x27, 0, 643f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 644f5271101SLydia Wang } 645eb7188caSLydia Wang } else if (spec->codec_type == VT1718S) { 646eb7188caSLydia Wang /* MUX6 (1eh) = stereo mixer */ 647eb7188caSLydia Wang imux_is_smixer = snd_hda_codec_read( 648eb7188caSLydia Wang codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 649eb7188caSLydia Wang /* inputs */ 650eb7188caSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 651eb7188caSLydia Wang parm = AC_PWRST_D3; 652eb7188caSLydia Wang set_pin_power_state(codec, 0x29, &parm); 653eb7188caSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 654eb7188caSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 655eb7188caSLydia Wang if (imux_is_smixer) 656eb7188caSLydia Wang parm = AC_PWRST_D0; 657eb7188caSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 658eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, 659eb7188caSLydia Wang parm); 660eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, 661eb7188caSLydia Wang parm); 662eb7188caSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 663eb7188caSLydia Wang parm); 664eb7188caSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, 665eb7188caSLydia Wang parm); 666eb7188caSLydia Wang 667eb7188caSLydia Wang /* outputs */ 668eb7188caSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 669eb7188caSLydia Wang parm = AC_PWRST_D3; 670eb7188caSLydia Wang set_pin_power_state(codec, 0x27, &parm); 671eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 672eb7188caSLydia Wang parm); 673eb7188caSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, 674eb7188caSLydia Wang parm); 675eb7188caSLydia Wang 676eb7188caSLydia Wang /* PW2 (26h), AOW2 (ah) */ 677eb7188caSLydia Wang parm = AC_PWRST_D3; 678eb7188caSLydia Wang set_pin_power_state(codec, 0x26, &parm); 679eb7188caSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, 680eb7188caSLydia Wang parm); 681eb7188caSLydia Wang 682eb7188caSLydia Wang /* PW0/1 (24h/25h) */ 683eb7188caSLydia Wang parm = AC_PWRST_D3; 684eb7188caSLydia Wang set_pin_power_state(codec, 0x24, &parm); 685eb7188caSLydia Wang set_pin_power_state(codec, 0x25, &parm); 686eb7188caSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 687eb7188caSLydia Wang set_pin_power_state(codec, 0x28, &parm); 688eb7188caSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, 689eb7188caSLydia Wang parm); 690eb7188caSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, 691eb7188caSLydia Wang parm); 692eb7188caSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 693eb7188caSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 694eb7188caSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 695eb7188caSLydia Wang if (spec->hp_independent_mode) { 696eb7188caSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 697eb7188caSLydia Wang parm = AC_PWRST_D3; 698eb7188caSLydia Wang set_pin_power_state(codec, 0x28, &parm); 699eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 700eb7188caSLydia Wang AC_VERB_SET_POWER_STATE, parm); 701eb7188caSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 702eb7188caSLydia Wang AC_VERB_SET_POWER_STATE, parm); 703eb7188caSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 704eb7188caSLydia Wang AC_VERB_SET_POWER_STATE, parm); 705eb7188caSLydia Wang } 706f3db423dSLydia Wang } else if (spec->codec_type == VT1716S) { 707f3db423dSLydia Wang unsigned int mono_out, present; 708f3db423dSLydia Wang /* SW0 (17h) = stereo mixer */ 709f3db423dSLydia Wang imux_is_smixer = snd_hda_codec_read( 710f3db423dSLydia Wang codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 711f3db423dSLydia Wang /* inputs */ 712f3db423dSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 713f3db423dSLydia Wang parm = AC_PWRST_D3; 714f3db423dSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 715f3db423dSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 716f3db423dSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 717f3db423dSLydia Wang if (imux_is_smixer) 718f3db423dSLydia Wang parm = AC_PWRST_D0; 719f3db423dSLydia Wang /* SW0 (17h), AIW0(13h) */ 720f3db423dSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, 721f3db423dSLydia Wang parm); 722f3db423dSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, 723f3db423dSLydia Wang parm); 724f3db423dSLydia Wang 725f3db423dSLydia Wang parm = AC_PWRST_D3; 726f3db423dSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 727f3db423dSLydia Wang /* PW11 (22h) */ 728f3db423dSLydia Wang if (spec->dmic_enabled) 729f3db423dSLydia Wang set_pin_power_state(codec, 0x22, &parm); 730f3db423dSLydia Wang else 731f3db423dSLydia Wang snd_hda_codec_write( 732f3db423dSLydia Wang codec, 0x22, 0, 733f3db423dSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 734f3db423dSLydia Wang 735f3db423dSLydia Wang /* SW2(26h), AIW1(14h) */ 736f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, 737f3db423dSLydia Wang parm); 738f3db423dSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, 739f3db423dSLydia Wang parm); 740f3db423dSLydia Wang 741f3db423dSLydia Wang /* outputs */ 742f3db423dSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 743f3db423dSLydia Wang parm = AC_PWRST_D3; 744f3db423dSLydia Wang set_pin_power_state(codec, 0x19, &parm); 745f3db423dSLydia Wang /* Smart 5.1 PW2(1bh) */ 746f3db423dSLydia Wang if (spec->smart51_enabled) 747f3db423dSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 748f3db423dSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, 749f3db423dSLydia Wang parm); 750f3db423dSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, 751f3db423dSLydia Wang parm); 752f3db423dSLydia Wang 753f3db423dSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 754f3db423dSLydia Wang parm = AC_PWRST_D3; 755f3db423dSLydia Wang set_pin_power_state(codec, 0x23, &parm); 756f3db423dSLydia Wang /* Smart 5.1 PW1(1ah) */ 757f3db423dSLydia Wang if (spec->smart51_enabled) 758f3db423dSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 759f3db423dSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, 760f3db423dSLydia Wang parm); 761f3db423dSLydia Wang 762f3db423dSLydia Wang /* Smart 5.1 PW5(1eh) */ 763f3db423dSLydia Wang if (spec->smart51_enabled) 764f3db423dSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 765f3db423dSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, 766f3db423dSLydia Wang parm); 767f3db423dSLydia Wang 768f3db423dSLydia Wang /* Mono out */ 769f3db423dSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 770f3db423dSLydia Wang present = snd_hda_codec_read( 771f3db423dSLydia Wang codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 772f3db423dSLydia Wang if (present) 773f3db423dSLydia Wang mono_out = 0; 774f3db423dSLydia Wang else { 775f3db423dSLydia Wang present = snd_hda_codec_read( 776f3db423dSLydia Wang codec, 0x1d, 0, AC_VERB_GET_PIN_SENSE, 0) 777f3db423dSLydia Wang & 0x80000000; 778f3db423dSLydia Wang if (!spec->hp_independent_mode && present) 779f3db423dSLydia Wang mono_out = 0; 780f3db423dSLydia Wang else 781f3db423dSLydia Wang mono_out = 1; 782f3db423dSLydia Wang } 783f3db423dSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 784f3db423dSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, 785f3db423dSLydia Wang parm); 786f3db423dSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, 787f3db423dSLydia Wang parm); 788f3db423dSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, 789f3db423dSLydia Wang parm); 790f3db423dSLydia Wang 791f3db423dSLydia Wang /* PW 3/4 (1ch/1dh) */ 792f3db423dSLydia Wang parm = AC_PWRST_D3; 793f3db423dSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 794f3db423dSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 795f3db423dSLydia Wang /* HP Independent Mode, power on AOW3 */ 796f3db423dSLydia Wang if (spec->hp_independent_mode) 797f3db423dSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 798f3db423dSLydia Wang AC_VERB_SET_POWER_STATE, parm); 799f3db423dSLydia Wang 800f3db423dSLydia Wang /* force to D0 for internal Speaker */ 801f3db423dSLydia Wang /* MW0 (16h), AOW0 (10h) */ 802f3db423dSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 803f3db423dSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 804f3db423dSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 805f3db423dSLydia Wang mono_out ? AC_PWRST_D0 : parm); 806*25eaba2fSLydia Wang } else if (spec->codec_type == VT2002P) { 807*25eaba2fSLydia Wang unsigned int present; 808*25eaba2fSLydia Wang /* MUX9 (1eh) = stereo mixer */ 809*25eaba2fSLydia Wang imux_is_smixer = snd_hda_codec_read( 810*25eaba2fSLydia Wang codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 811*25eaba2fSLydia Wang /* inputs */ 812*25eaba2fSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 813*25eaba2fSLydia Wang parm = AC_PWRST_D3; 814*25eaba2fSLydia Wang set_pin_power_state(codec, 0x29, &parm); 815*25eaba2fSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 816*25eaba2fSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 817*25eaba2fSLydia Wang if (imux_is_smixer) 818*25eaba2fSLydia Wang parm = AC_PWRST_D0; 819*25eaba2fSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 820*25eaba2fSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, 821*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 822*25eaba2fSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, 823*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 824*25eaba2fSLydia Wang snd_hda_codec_write(codec, 0x10, 0, 825*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 826*25eaba2fSLydia Wang snd_hda_codec_write(codec, 0x11, 0, 827*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 828*25eaba2fSLydia Wang 829*25eaba2fSLydia Wang /* outputs */ 830*25eaba2fSLydia Wang /* AOW0 (8h)*/ 831*25eaba2fSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 832*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 833*25eaba2fSLydia Wang 834*25eaba2fSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 835*25eaba2fSLydia Wang parm = AC_PWRST_D3; 836*25eaba2fSLydia Wang set_pin_power_state(codec, 0x26, &parm); 837*25eaba2fSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 838*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 839*25eaba2fSLydia Wang snd_hda_codec_write(codec, 0x37, 840*25eaba2fSLydia Wang 0, AC_VERB_SET_POWER_STATE, parm); 841*25eaba2fSLydia Wang 842*25eaba2fSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 843*25eaba2fSLydia Wang parm = AC_PWRST_D3; 844*25eaba2fSLydia Wang set_pin_power_state(codec, 0x25, &parm); 845*25eaba2fSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 846*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 847*25eaba2fSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 848*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 849*25eaba2fSLydia Wang if (spec->hp_independent_mode) { 850*25eaba2fSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 851*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 852*25eaba2fSLydia Wang } 853*25eaba2fSLydia Wang 854*25eaba2fSLydia Wang /* Class-D */ 855*25eaba2fSLydia Wang /* PW0 (24h), MW0(18h), MUX0(34h) */ 856*25eaba2fSLydia Wang present = snd_hda_codec_read( 857*25eaba2fSLydia Wang codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 858*25eaba2fSLydia Wang parm = AC_PWRST_D3; 859*25eaba2fSLydia Wang set_pin_power_state(codec, 0x24, &parm); 860*25eaba2fSLydia Wang if (present) { 861*25eaba2fSLydia Wang snd_hda_codec_write( 862*25eaba2fSLydia Wang codec, 0x18, 0, 863*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 864*25eaba2fSLydia Wang snd_hda_codec_write( 865*25eaba2fSLydia Wang codec, 0x34, 0, 866*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 867*25eaba2fSLydia Wang } else { 868*25eaba2fSLydia Wang snd_hda_codec_write( 869*25eaba2fSLydia Wang codec, 0x18, 0, 870*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 871*25eaba2fSLydia Wang snd_hda_codec_write( 872*25eaba2fSLydia Wang codec, 0x34, 0, 873*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 874*25eaba2fSLydia Wang } 875*25eaba2fSLydia Wang 876*25eaba2fSLydia Wang /* Mono Out */ 877*25eaba2fSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 878*25eaba2fSLydia Wang present = snd_hda_codec_read( 879*25eaba2fSLydia Wang codec, 0x26, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 880*25eaba2fSLydia Wang parm = AC_PWRST_D3; 881*25eaba2fSLydia Wang set_pin_power_state(codec, 0x31, &parm); 882*25eaba2fSLydia Wang if (present) { 883*25eaba2fSLydia Wang snd_hda_codec_write( 884*25eaba2fSLydia Wang codec, 0x17, 0, 885*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 886*25eaba2fSLydia Wang snd_hda_codec_write( 887*25eaba2fSLydia Wang codec, 0x3b, 0, 888*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 889*25eaba2fSLydia Wang } else { 890*25eaba2fSLydia Wang snd_hda_codec_write( 891*25eaba2fSLydia Wang codec, 0x17, 0, 892*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 893*25eaba2fSLydia Wang snd_hda_codec_write( 894*25eaba2fSLydia Wang codec, 0x3b, 0, 895*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 896*25eaba2fSLydia Wang } 897*25eaba2fSLydia Wang 898*25eaba2fSLydia Wang /* MW9 (21h) */ 899*25eaba2fSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 900*25eaba2fSLydia Wang snd_hda_codec_write( 901*25eaba2fSLydia Wang codec, 0x21, 0, 902*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 903*25eaba2fSLydia Wang else 904*25eaba2fSLydia Wang snd_hda_codec_write( 905*25eaba2fSLydia Wang codec, 0x21, 0, 906*25eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 907f5271101SLydia Wang } 908f5271101SLydia Wang } 909f5271101SLydia Wang 910c577b8a1SJoseph Chan /* 911c577b8a1SJoseph Chan * input MUX handling 912c577b8a1SJoseph Chan */ 913c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 914c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 915c577b8a1SJoseph Chan { 916c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 917c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 918c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 919c577b8a1SJoseph Chan } 920c577b8a1SJoseph Chan 921c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 922c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 923c577b8a1SJoseph Chan { 924c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 925c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 926c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 927c577b8a1SJoseph Chan 928c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 929c577b8a1SJoseph Chan return 0; 930c577b8a1SJoseph Chan } 931c577b8a1SJoseph Chan 932c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 933c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 934c577b8a1SJoseph Chan { 935c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 936c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 937c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 938c577b8a1SJoseph Chan 939337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 940337b9d02STakashi Iwai return -EINVAL; 941a80e6e3cSLydia Wang /* switch to D0 beofre change index */ 942a80e6e3cSLydia Wang if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, 943a80e6e3cSLydia Wang AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 944a80e6e3cSLydia Wang snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 945a80e6e3cSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 946a80e6e3cSLydia Wang /* update jack power state */ 947a80e6e3cSLydia Wang set_jack_power_state(codec); 948a80e6e3cSLydia Wang 949c577b8a1SJoseph Chan return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 950337b9d02STakashi Iwai spec->mux_nids[adc_idx], 951c577b8a1SJoseph Chan &spec->cur_mux[adc_idx]); 952c577b8a1SJoseph Chan } 953c577b8a1SJoseph Chan 9540aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 9550aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 9560aa62aefSHarald Welte { 9570aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9580aa62aefSHarald Welte struct via_spec *spec = codec->spec; 9590aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 9600aa62aefSHarald Welte } 9610aa62aefSHarald Welte 9620aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 9630aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 9640aa62aefSHarald Welte { 9650aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9660aa62aefSHarald Welte struct via_spec *spec = codec->spec; 967eb7188caSLydia Wang hda_nid_t nid; 968eb7188caSLydia Wang unsigned int pinsel; 969eb7188caSLydia Wang 970eb7188caSLydia Wang switch (spec->codec_type) { 971eb7188caSLydia Wang case VT1718S: 972eb7188caSLydia Wang nid = 0x34; 973eb7188caSLydia Wang break; 974*25eaba2fSLydia Wang case VT2002P: 975*25eaba2fSLydia Wang nid = 0x35; 976*25eaba2fSLydia Wang break; 977eb7188caSLydia Wang default: 978eb7188caSLydia Wang nid = spec->autocfg.hp_pins[0]; 979eb7188caSLydia Wang break; 980eb7188caSLydia Wang } 981eb7188caSLydia Wang /* use !! to translate conn sel 2 for VT1718S */ 982eb7188caSLydia Wang pinsel = !!snd_hda_codec_read(codec, nid, 0, 9830aa62aefSHarald Welte AC_VERB_GET_CONNECT_SEL, 9840aa62aefSHarald Welte 0x00); 9850aa62aefSHarald Welte ucontrol->value.enumerated.item[0] = pinsel; 9860aa62aefSHarald Welte 9870aa62aefSHarald Welte return 0; 9880aa62aefSHarald Welte } 9890aa62aefSHarald Welte 9900713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active) 9910713efebSLydia Wang { 9920713efebSLydia Wang struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); 9930713efebSLydia Wang if (ctl) { 9940713efebSLydia Wang ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 9950713efebSLydia Wang ctl->vd[0].access |= active 9960713efebSLydia Wang ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; 9970713efebSLydia Wang snd_ctl_notify(codec->bus->card, 9980713efebSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); 9990713efebSLydia Wang } 10000713efebSLydia Wang } 10010713efebSLydia Wang 1002cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec) 1003cdc1784dSLydia Wang { 1004cdc1784dSLydia Wang /* mute side channel */ 1005cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 1006cdc1784dSLydia Wang unsigned int parm = spec->hp_independent_mode 1007cdc1784dSLydia Wang ? AMP_OUT_MUTE : AMP_OUT_UNMUTE; 1008cdc1784dSLydia Wang hda_nid_t sw3; 1009cdc1784dSLydia Wang 1010cdc1784dSLydia Wang switch (spec->codec_type) { 1011cdc1784dSLydia Wang case VT1708: 1012cdc1784dSLydia Wang sw3 = 0x1b; 1013cdc1784dSLydia Wang break; 1014cdc1784dSLydia Wang case VT1709_10CH: 1015cdc1784dSLydia Wang sw3 = 0x29; 1016cdc1784dSLydia Wang break; 1017cdc1784dSLydia Wang case VT1708B_8CH: 1018cdc1784dSLydia Wang case VT1708S: 1019cdc1784dSLydia Wang sw3 = 0x27; 1020cdc1784dSLydia Wang break; 1021cdc1784dSLydia Wang default: 1022cdc1784dSLydia Wang sw3 = 0; 1023cdc1784dSLydia Wang break; 1024cdc1784dSLydia Wang } 1025cdc1784dSLydia Wang 1026cdc1784dSLydia Wang if (sw3) 1027cdc1784dSLydia Wang snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE, 1028cdc1784dSLydia Wang parm); 1029cdc1784dSLydia Wang return 0; 1030cdc1784dSLydia Wang } 1031cdc1784dSLydia Wang 10320aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 10330aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 10340aa62aefSHarald Welte { 10350aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 10360aa62aefSHarald Welte struct via_spec *spec = codec->spec; 10370aa62aefSHarald Welte hda_nid_t nid = spec->autocfg.hp_pins[0]; 10380aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 1039cdc1784dSLydia Wang /* Get Independent Mode index of headphone pin widget */ 1040cdc1784dSLydia Wang spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel 1041cdc1784dSLydia Wang ? 1 : 0; 10420aa62aefSHarald Welte 1043eb7188caSLydia Wang switch (spec->codec_type) { 1044eb7188caSLydia Wang case VT1718S: 1045eb7188caSLydia Wang nid = 0x34; 1046eb7188caSLydia Wang pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */ 1047eb7188caSLydia Wang spec->multiout.num_dacs = 4; 1048eb7188caSLydia Wang break; 1049*25eaba2fSLydia Wang case VT2002P: 1050*25eaba2fSLydia Wang nid = 0x35; 1051*25eaba2fSLydia Wang break; 1052eb7188caSLydia Wang default: 1053eb7188caSLydia Wang nid = spec->autocfg.hp_pins[0]; 1054eb7188caSLydia Wang break; 1055eb7188caSLydia Wang } 1056cdc1784dSLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); 10570aa62aefSHarald Welte 1058cdc1784dSLydia Wang if (spec->multiout.hp_nid && spec->multiout.hp_nid 1059cdc1784dSLydia Wang != spec->multiout.dac_nids[HDA_FRONT]) 1060cdc1784dSLydia Wang snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, 10610aa62aefSHarald Welte 0, 0, 0); 10620aa62aefSHarald Welte 1063cdc1784dSLydia Wang update_side_mute_status(codec); 10640713efebSLydia Wang /* update HP volume/swtich active state */ 10650713efebSLydia Wang if (spec->codec_type == VT1708S 1066eb7188caSLydia Wang || spec->codec_type == VT1702 1067f3db423dSLydia Wang || spec->codec_type == VT1718S 1068*25eaba2fSLydia Wang || spec->codec_type == VT1716S 1069*25eaba2fSLydia Wang || spec->codec_type == VT2002P) { 10700713efebSLydia Wang activate_ctl(codec, "Headphone Playback Volume", 10710713efebSLydia Wang spec->hp_independent_mode); 10720713efebSLydia Wang activate_ctl(codec, "Headphone Playback Switch", 10730713efebSLydia Wang spec->hp_independent_mode); 10740713efebSLydia Wang } 10750aa62aefSHarald Welte return 0; 10760aa62aefSHarald Welte } 10770aa62aefSHarald Welte 10780aa62aefSHarald Welte static struct snd_kcontrol_new via_hp_mixer[] = { 10790aa62aefSHarald Welte { 10800aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 10810aa62aefSHarald Welte .name = "Independent HP", 10820aa62aefSHarald Welte .count = 1, 10830aa62aefSHarald Welte .info = via_independent_hp_info, 10840aa62aefSHarald Welte .get = via_independent_hp_get, 10850aa62aefSHarald Welte .put = via_independent_hp_put, 10860aa62aefSHarald Welte }, 10870aa62aefSHarald Welte { } /* end */ 10880aa62aefSHarald Welte }; 10890aa62aefSHarald Welte 10901564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 10911564b287SLydia Wang { 10921564b287SLydia Wang int i; 10931564b287SLydia Wang struct snd_ctl_elem_id id; 10941564b287SLydia Wang const char *labels[] = {"Mic", "Front Mic", "Line"}; 10951564b287SLydia Wang 10961564b287SLydia Wang memset(&id, 0, sizeof(id)); 10971564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 10981564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(labels); i++) { 10991564b287SLydia Wang sprintf(id.name, "%s Playback Volume", labels[i]); 11001564b287SLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 11011564b287SLydia Wang &id); 11021564b287SLydia Wang } 11031564b287SLydia Wang } 11041564b287SLydia Wang 11051564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 11061564b287SLydia Wang { 11071564b287SLydia Wang struct via_spec *spec = codec->spec; 11081564b287SLydia Wang hda_nid_t nid_mixer; 11091564b287SLydia Wang int start_idx; 11101564b287SLydia Wang int end_idx; 11111564b287SLydia Wang int i; 11121564b287SLydia Wang /* get nid of MW0 and start & end index */ 11131564b287SLydia Wang switch (spec->codec_type) { 11141564b287SLydia Wang case VT1708: 11151564b287SLydia Wang nid_mixer = 0x17; 11161564b287SLydia Wang start_idx = 2; 11171564b287SLydia Wang end_idx = 4; 11181564b287SLydia Wang break; 11191564b287SLydia Wang case VT1709_10CH: 11201564b287SLydia Wang case VT1709_6CH: 11211564b287SLydia Wang nid_mixer = 0x18; 11221564b287SLydia Wang start_idx = 2; 11231564b287SLydia Wang end_idx = 4; 11241564b287SLydia Wang break; 11251564b287SLydia Wang case VT1708B_8CH: 11261564b287SLydia Wang case VT1708B_4CH: 11271564b287SLydia Wang case VT1708S: 1128f3db423dSLydia Wang case VT1716S: 11291564b287SLydia Wang nid_mixer = 0x16; 11301564b287SLydia Wang start_idx = 2; 11311564b287SLydia Wang end_idx = 4; 11321564b287SLydia Wang break; 11331564b287SLydia Wang default: 11341564b287SLydia Wang return; 11351564b287SLydia Wang } 11361564b287SLydia Wang /* check AA path's mute status */ 11371564b287SLydia Wang for (i = start_idx; i <= end_idx; i++) { 11381564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 11391564b287SLydia Wang snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i, 11401564b287SLydia Wang HDA_AMP_MUTE, val); 11411564b287SLydia Wang } 11421564b287SLydia Wang } 11431564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) 11441564b287SLydia Wang { 11451564b287SLydia Wang int res = 0; 11461564b287SLydia Wang int index; 11471564b287SLydia Wang for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) { 11481564b287SLydia Wang if (pin == spec->autocfg.input_pins[index]) { 11491564b287SLydia Wang res = 1; 11501564b287SLydia Wang break; 11511564b287SLydia Wang } 11521564b287SLydia Wang } 11531564b287SLydia Wang return res; 11541564b287SLydia Wang } 11551564b287SLydia Wang 11561564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol, 11571564b287SLydia Wang struct snd_ctl_elem_info *uinfo) 11581564b287SLydia Wang { 11591564b287SLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 11601564b287SLydia Wang uinfo->count = 1; 11611564b287SLydia Wang uinfo->value.integer.min = 0; 11621564b287SLydia Wang uinfo->value.integer.max = 1; 11631564b287SLydia Wang return 0; 11641564b287SLydia Wang } 11651564b287SLydia Wang 11661564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 11671564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 11681564b287SLydia Wang { 11691564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 11701564b287SLydia Wang struct via_spec *spec = codec->spec; 11711564b287SLydia Wang int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; 11721564b287SLydia Wang int on = 1; 11731564b287SLydia Wang int i; 11741564b287SLydia Wang 11751564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(index); i++) { 11761564b287SLydia Wang hda_nid_t nid = spec->autocfg.input_pins[index[i]]; 11771564b287SLydia Wang if (nid) { 11781564b287SLydia Wang int ctl = 11791564b287SLydia Wang snd_hda_codec_read(codec, nid, 0, 11801564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 11811564b287SLydia Wang 0); 11821564b287SLydia Wang if (i == AUTO_PIN_FRONT_MIC 1183eb7188caSLydia Wang && spec->hp_independent_mode 1184eb7188caSLydia Wang && spec->codec_type != VT1718S) 11851564b287SLydia Wang continue; /* ignore FMic for independent HP */ 11861564b287SLydia Wang if (ctl & AC_PINCTL_IN_EN 11871564b287SLydia Wang && !(ctl & AC_PINCTL_OUT_EN)) 11881564b287SLydia Wang on = 0; 11891564b287SLydia Wang } 11901564b287SLydia Wang } 11911564b287SLydia Wang *ucontrol->value.integer.value = on; 11921564b287SLydia Wang return 0; 11931564b287SLydia Wang } 11941564b287SLydia Wang 11951564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 11961564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 11971564b287SLydia Wang { 11981564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 11991564b287SLydia Wang struct via_spec *spec = codec->spec; 12001564b287SLydia Wang int out_in = *ucontrol->value.integer.value 12011564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 12021564b287SLydia Wang int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; 12031564b287SLydia Wang int i; 12041564b287SLydia Wang 12051564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(index); i++) { 12061564b287SLydia Wang hda_nid_t nid = spec->autocfg.input_pins[index[i]]; 12071564b287SLydia Wang if (i == AUTO_PIN_FRONT_MIC 1208eb7188caSLydia Wang && spec->hp_independent_mode 1209eb7188caSLydia Wang && spec->codec_type != VT1718S) 12101564b287SLydia Wang continue; /* don't retask FMic for independent HP */ 12111564b287SLydia Wang if (nid) { 12121564b287SLydia Wang unsigned int parm = snd_hda_codec_read( 12131564b287SLydia Wang codec, nid, 0, 12141564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 12151564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 12161564b287SLydia Wang parm |= out_in; 12171564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 12181564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 12191564b287SLydia Wang parm); 12201564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 12211564b287SLydia Wang mute_aa_path(codec, 1); 12221564b287SLydia Wang notify_aa_path_ctls(codec); 12231564b287SLydia Wang } 1224eb7188caSLydia Wang if (spec->codec_type == VT1718S) 1225eb7188caSLydia Wang snd_hda_codec_amp_stereo( 1226eb7188caSLydia Wang codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, 1227eb7188caSLydia Wang HDA_AMP_UNMUTE); 12281564b287SLydia Wang } 12291564b287SLydia Wang if (i == AUTO_PIN_FRONT_MIC) { 1230f3db423dSLydia Wang if (spec->codec_type == VT1708S 1231f3db423dSLydia Wang || spec->codec_type == VT1716S) { 12321564b287SLydia Wang /* input = index 1 (AOW3) */ 12331564b287SLydia Wang snd_hda_codec_write( 12341564b287SLydia Wang codec, nid, 0, 12351564b287SLydia Wang AC_VERB_SET_CONNECT_SEL, 1); 12361564b287SLydia Wang snd_hda_codec_amp_stereo( 12371564b287SLydia Wang codec, nid, HDA_OUTPUT, 12381564b287SLydia Wang 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); 12391564b287SLydia Wang } 12401564b287SLydia Wang } 12411564b287SLydia Wang } 12421564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 12431564b287SLydia Wang set_jack_power_state(codec); 12441564b287SLydia Wang return 1; 12451564b287SLydia Wang } 12461564b287SLydia Wang 12471564b287SLydia Wang static struct snd_kcontrol_new via_smart51_mixer[] = { 12481564b287SLydia Wang { 12491564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 12501564b287SLydia Wang .name = "Smart 5.1", 12511564b287SLydia Wang .count = 1, 12521564b287SLydia Wang .info = via_smart51_info, 12531564b287SLydia Wang .get = via_smart51_get, 12541564b287SLydia Wang .put = via_smart51_put, 12551564b287SLydia Wang }, 12561564b287SLydia Wang {} /* end */ 12571564b287SLydia Wang }; 12581564b287SLydia Wang 1259c577b8a1SJoseph Chan /* capture mixer elements */ 1260c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_capture_mixer[] = { 1261c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), 1262c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), 1263c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), 1264c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), 1265c577b8a1SJoseph Chan { 1266c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1267c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 1268c577b8a1SJoseph Chan * So call somewhat different.. 1269c577b8a1SJoseph Chan */ 1270c577b8a1SJoseph Chan /* .name = "Capture Source", */ 1271c577b8a1SJoseph Chan .name = "Input Source", 1272c577b8a1SJoseph Chan .count = 1, 1273c577b8a1SJoseph Chan .info = via_mux_enum_info, 1274c577b8a1SJoseph Chan .get = via_mux_enum_get, 1275c577b8a1SJoseph Chan .put = via_mux_enum_put, 1276c577b8a1SJoseph Chan }, 1277c577b8a1SJoseph Chan { } /* end */ 1278c577b8a1SJoseph Chan }; 1279f5271101SLydia Wang 1280f5271101SLydia Wang /* check AA path's mute statue */ 1281f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec) 1282f5271101SLydia Wang { 1283f5271101SLydia Wang int mute = 1; 1284f5271101SLydia Wang hda_nid_t nid_mixer; 1285f5271101SLydia Wang int start_idx; 1286f5271101SLydia Wang int end_idx; 1287f5271101SLydia Wang int i; 1288f5271101SLydia Wang struct via_spec *spec = codec->spec; 1289f5271101SLydia Wang /* get nid of MW0 and start & end index */ 1290f5271101SLydia Wang switch (spec->codec_type) { 1291f5271101SLydia Wang case VT1708B_8CH: 1292f5271101SLydia Wang case VT1708B_4CH: 1293f5271101SLydia Wang case VT1708S: 1294f3db423dSLydia Wang case VT1716S: 1295f5271101SLydia Wang nid_mixer = 0x16; 1296f5271101SLydia Wang start_idx = 2; 1297f5271101SLydia Wang end_idx = 4; 1298f5271101SLydia Wang break; 1299f5271101SLydia Wang case VT1702: 1300f5271101SLydia Wang nid_mixer = 0x1a; 1301f5271101SLydia Wang start_idx = 1; 1302f5271101SLydia Wang end_idx = 3; 1303f5271101SLydia Wang break; 1304eb7188caSLydia Wang case VT1718S: 1305eb7188caSLydia Wang nid_mixer = 0x21; 1306eb7188caSLydia Wang start_idx = 1; 1307eb7188caSLydia Wang end_idx = 3; 1308eb7188caSLydia Wang break; 1309*25eaba2fSLydia Wang case VT2002P: 1310*25eaba2fSLydia Wang nid_mixer = 0x21; 1311*25eaba2fSLydia Wang start_idx = 0; 1312*25eaba2fSLydia Wang end_idx = 2; 1313*25eaba2fSLydia Wang break; 1314f5271101SLydia Wang default: 1315f5271101SLydia Wang return 0; 1316f5271101SLydia Wang } 1317f5271101SLydia Wang /* check AA path's mute status */ 1318f5271101SLydia Wang for (i = start_idx; i <= end_idx; i++) { 1319f5271101SLydia Wang unsigned int con_list = snd_hda_codec_read( 1320f5271101SLydia Wang codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); 1321f5271101SLydia Wang int shift = 8 * (i % 4); 1322f5271101SLydia Wang hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; 1323f5271101SLydia Wang unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); 1324f5271101SLydia Wang if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { 1325f5271101SLydia Wang /* check mute status while the pin is connected */ 1326f5271101SLydia Wang int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, 1327f5271101SLydia Wang HDA_INPUT, i) >> 7; 1328f5271101SLydia Wang int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, 1329f5271101SLydia Wang HDA_INPUT, i) >> 7; 1330f5271101SLydia Wang if (!mute_l || !mute_r) { 1331f5271101SLydia Wang mute = 0; 1332f5271101SLydia Wang break; 1333f5271101SLydia Wang } 1334f5271101SLydia Wang } 1335f5271101SLydia Wang } 1336f5271101SLydia Wang return mute; 1337f5271101SLydia Wang } 1338f5271101SLydia Wang 1339f5271101SLydia Wang /* enter/exit analog low-current mode */ 1340f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) 1341f5271101SLydia Wang { 1342f5271101SLydia Wang struct via_spec *spec = codec->spec; 1343f5271101SLydia Wang static int saved_stream_idle = 1; /* saved stream idle status */ 1344f5271101SLydia Wang int enable = is_aa_path_mute(codec); 1345f5271101SLydia Wang unsigned int verb = 0; 1346f5271101SLydia Wang unsigned int parm = 0; 1347f5271101SLydia Wang 1348f5271101SLydia Wang if (stream_idle == -1) /* stream status did not change */ 1349f5271101SLydia Wang enable = enable && saved_stream_idle; 1350f5271101SLydia Wang else { 1351f5271101SLydia Wang enable = enable && stream_idle; 1352f5271101SLydia Wang saved_stream_idle = stream_idle; 1353f5271101SLydia Wang } 1354f5271101SLydia Wang 1355f5271101SLydia Wang /* decide low current mode's verb & parameter */ 1356f5271101SLydia Wang switch (spec->codec_type) { 1357f5271101SLydia Wang case VT1708B_8CH: 1358f5271101SLydia Wang case VT1708B_4CH: 1359f5271101SLydia Wang verb = 0xf70; 1360f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 1361f5271101SLydia Wang break; 1362f5271101SLydia Wang case VT1708S: 1363eb7188caSLydia Wang case VT1718S: 1364f3db423dSLydia Wang case VT1716S: 1365f5271101SLydia Wang verb = 0xf73; 1366f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 1367f5271101SLydia Wang break; 1368f5271101SLydia Wang case VT1702: 1369f5271101SLydia Wang verb = 0xf73; 1370f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 1371f5271101SLydia Wang break; 1372*25eaba2fSLydia Wang case VT2002P: 1373*25eaba2fSLydia Wang verb = 0xf93; 1374*25eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 1375*25eaba2fSLydia Wang break; 1376f5271101SLydia Wang default: 1377f5271101SLydia Wang return; /* other codecs are not supported */ 1378f5271101SLydia Wang } 1379f5271101SLydia Wang /* send verb */ 1380f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 1381f5271101SLydia Wang } 1382f5271101SLydia Wang 1383c577b8a1SJoseph Chan /* 1384c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1385c577b8a1SJoseph Chan */ 1386c577b8a1SJoseph Chan static struct hda_verb vt1708_volume_init_verbs[] = { 1387c577b8a1SJoseph Chan /* 1388c577b8a1SJoseph Chan * Unmute ADC0-1 and set the default input to mic-in 1389c577b8a1SJoseph Chan */ 1390c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1391c577b8a1SJoseph Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1392c577b8a1SJoseph Chan 1393c577b8a1SJoseph Chan 1394f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1395c577b8a1SJoseph Chan * mixer widget 1396c577b8a1SJoseph Chan */ 1397c577b8a1SJoseph Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1398f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1399f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1400f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1401f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1402f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1403c577b8a1SJoseph Chan 1404c577b8a1SJoseph Chan /* 1405c577b8a1SJoseph Chan * Set up output mixers (0x19 - 0x1b) 1406c577b8a1SJoseph Chan */ 1407c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 1408c577b8a1SJoseph Chan {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1409c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1410c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1411c577b8a1SJoseph Chan 1412c577b8a1SJoseph Chan /* Setup default input to PW4 */ 1413c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, 1414c577b8a1SJoseph Chan /* PW9 Output enable */ 1415c577b8a1SJoseph Chan {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1416f7278fd0SJosepch Chan { } 1417c577b8a1SJoseph Chan }; 1418c577b8a1SJoseph Chan 1419c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, 1420c577b8a1SJoseph Chan struct hda_codec *codec, 1421c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1422c577b8a1SJoseph Chan { 1423c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 142417314379SLydia Wang int idle = substream->pstr->substream_opened == 1 142517314379SLydia Wang && substream->ref_count == 0; 142617314379SLydia Wang analog_low_current_mode(codec, idle); 14279a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 14289a08160bSTakashi Iwai hinfo); 1429c577b8a1SJoseph Chan } 1430c577b8a1SJoseph Chan 14310aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec, 14320aa62aefSHarald Welte unsigned int stream_tag, 14330aa62aefSHarald Welte unsigned int format, 14340aa62aefSHarald Welte struct snd_pcm_substream *substream) 14350aa62aefSHarald Welte { 14360aa62aefSHarald Welte struct via_spec *spec = codec->spec; 14370aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 14380aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 14390aa62aefSHarald Welte int chs = substream->runtime->channels; 14400aa62aefSHarald Welte int i; 14410aa62aefSHarald Welte 14420aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 14430aa62aefSHarald Welte if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { 14440aa62aefSHarald Welte if (chs == 2 && 14450aa62aefSHarald Welte snd_hda_is_supported_format(codec, mout->dig_out_nid, 14460aa62aefSHarald Welte format) && 14470aa62aefSHarald Welte !(codec->spdif_status & IEC958_AES0_NONAUDIO)) { 14480aa62aefSHarald Welte mout->dig_out_used = HDA_DIG_ANALOG_DUP; 14490aa62aefSHarald Welte /* turn off SPDIF once; otherwise the IEC958 bits won't 14500aa62aefSHarald Welte * be updated */ 14510aa62aefSHarald Welte if (codec->spdif_ctls & AC_DIG1_ENABLE) 14520aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 14530aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 14540aa62aefSHarald Welte codec->spdif_ctls & 14550aa62aefSHarald Welte ~AC_DIG1_ENABLE & 0xff); 14560aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 14570aa62aefSHarald Welte stream_tag, 0, format); 14580aa62aefSHarald Welte /* turn on again (if needed) */ 14590aa62aefSHarald Welte if (codec->spdif_ctls & AC_DIG1_ENABLE) 14600aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 14610aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 14620aa62aefSHarald Welte codec->spdif_ctls & 0xff); 14630aa62aefSHarald Welte } else { 14640aa62aefSHarald Welte mout->dig_out_used = 0; 14650aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 14660aa62aefSHarald Welte 0, 0, 0); 14670aa62aefSHarald Welte } 14680aa62aefSHarald Welte } 14690aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 14700aa62aefSHarald Welte 14710aa62aefSHarald Welte /* front */ 14720aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 14730aa62aefSHarald Welte 0, format); 14740aa62aefSHarald Welte 1475eb7188caSLydia Wang if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] 1476eb7188caSLydia Wang && !spec->hp_independent_mode) 14770aa62aefSHarald Welte /* headphone out will just decode front left/right (stereo) */ 14780aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 14790aa62aefSHarald Welte 0, format); 14800aa62aefSHarald Welte 14810aa62aefSHarald Welte /* extra outputs copied from front */ 14820aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 14830aa62aefSHarald Welte if (mout->extra_out_nid[i]) 14840aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 14850aa62aefSHarald Welte mout->extra_out_nid[i], 14860aa62aefSHarald Welte stream_tag, 0, format); 14870aa62aefSHarald Welte 14880aa62aefSHarald Welte /* surrounds */ 14890aa62aefSHarald Welte for (i = 1; i < mout->num_dacs; i++) { 14900aa62aefSHarald Welte if (chs >= (i + 1) * 2) /* independent out */ 14910aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 14920aa62aefSHarald Welte i * 2, format); 14930aa62aefSHarald Welte else /* copy front */ 14940aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 14950aa62aefSHarald Welte 0, format); 14960aa62aefSHarald Welte } 14970aa62aefSHarald Welte } 14980aa62aefSHarald Welte 14990aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 15000aa62aefSHarald Welte struct hda_codec *codec, 15010aa62aefSHarald Welte unsigned int stream_tag, 15020aa62aefSHarald Welte unsigned int format, 15030aa62aefSHarald Welte struct snd_pcm_substream *substream) 15040aa62aefSHarald Welte { 15050aa62aefSHarald Welte struct via_spec *spec = codec->spec; 15060aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 15070aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 15080aa62aefSHarald Welte 15090aa62aefSHarald Welte if (substream->number == 0) 15100aa62aefSHarald Welte playback_multi_pcm_prep_0(codec, stream_tag, format, 15110aa62aefSHarald Welte substream); 15120aa62aefSHarald Welte else { 15130aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 15140aa62aefSHarald Welte spec->hp_independent_mode) 15150aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 15160aa62aefSHarald Welte stream_tag, 0, format); 15170aa62aefSHarald Welte } 15181f2e99feSLydia Wang vt1708_start_hp_work(spec); 15190aa62aefSHarald Welte return 0; 15200aa62aefSHarald Welte } 15210aa62aefSHarald Welte 15220aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 15230aa62aefSHarald Welte struct hda_codec *codec, 15240aa62aefSHarald Welte struct snd_pcm_substream *substream) 15250aa62aefSHarald Welte { 15260aa62aefSHarald Welte struct via_spec *spec = codec->spec; 15270aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 15280aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 15290aa62aefSHarald Welte int i; 15300aa62aefSHarald Welte 15310aa62aefSHarald Welte if (substream->number == 0) { 15320aa62aefSHarald Welte for (i = 0; i < mout->num_dacs; i++) 15330aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); 15340aa62aefSHarald Welte 15350aa62aefSHarald Welte if (mout->hp_nid && !spec->hp_independent_mode) 15360aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 15370aa62aefSHarald Welte 0, 0, 0); 15380aa62aefSHarald Welte 15390aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 15400aa62aefSHarald Welte if (mout->extra_out_nid[i]) 15410aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 15420aa62aefSHarald Welte mout->extra_out_nid[i], 15430aa62aefSHarald Welte 0, 0, 0); 15440aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 15450aa62aefSHarald Welte if (mout->dig_out_nid && 15460aa62aefSHarald Welte mout->dig_out_used == HDA_DIG_ANALOG_DUP) { 15470aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 15480aa62aefSHarald Welte 0, 0, 0); 15490aa62aefSHarald Welte mout->dig_out_used = 0; 15500aa62aefSHarald Welte } 15510aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 15520aa62aefSHarald Welte } else { 15530aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 15540aa62aefSHarald Welte spec->hp_independent_mode) 15550aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 15560aa62aefSHarald Welte 0, 0, 0); 15570aa62aefSHarald Welte } 15581f2e99feSLydia Wang vt1708_stop_hp_work(spec); 15590aa62aefSHarald Welte return 0; 15600aa62aefSHarald Welte } 15610aa62aefSHarald Welte 1562c577b8a1SJoseph Chan /* 1563c577b8a1SJoseph Chan * Digital out 1564c577b8a1SJoseph Chan */ 1565c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1566c577b8a1SJoseph Chan struct hda_codec *codec, 1567c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1568c577b8a1SJoseph Chan { 1569c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1570c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1571c577b8a1SJoseph Chan } 1572c577b8a1SJoseph Chan 1573c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1574c577b8a1SJoseph Chan struct hda_codec *codec, 1575c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1576c577b8a1SJoseph Chan { 1577c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1578c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1579c577b8a1SJoseph Chan } 1580c577b8a1SJoseph Chan 15815691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 158298aa34c0SHarald Welte struct hda_codec *codec, 158398aa34c0SHarald Welte unsigned int stream_tag, 158498aa34c0SHarald Welte unsigned int format, 158598aa34c0SHarald Welte struct snd_pcm_substream *substream) 158698aa34c0SHarald Welte { 158798aa34c0SHarald Welte struct via_spec *spec = codec->spec; 15889da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 15899da29271STakashi Iwai stream_tag, format, substream); 15909da29271STakashi Iwai } 15915691ec7fSHarald Welte 15929da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 15939da29271STakashi Iwai struct hda_codec *codec, 15949da29271STakashi Iwai struct snd_pcm_substream *substream) 15959da29271STakashi Iwai { 15969da29271STakashi Iwai struct via_spec *spec = codec->spec; 15979da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 159898aa34c0SHarald Welte return 0; 159998aa34c0SHarald Welte } 160098aa34c0SHarald Welte 1601c577b8a1SJoseph Chan /* 1602c577b8a1SJoseph Chan * Analog capture 1603c577b8a1SJoseph Chan */ 1604c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1605c577b8a1SJoseph Chan struct hda_codec *codec, 1606c577b8a1SJoseph Chan unsigned int stream_tag, 1607c577b8a1SJoseph Chan unsigned int format, 1608c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1609c577b8a1SJoseph Chan { 1610c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1611c577b8a1SJoseph Chan 1612c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1613c577b8a1SJoseph Chan stream_tag, 0, format); 1614c577b8a1SJoseph Chan return 0; 1615c577b8a1SJoseph Chan } 1616c577b8a1SJoseph Chan 1617c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1618c577b8a1SJoseph Chan struct hda_codec *codec, 1619c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1620c577b8a1SJoseph Chan { 1621c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1622888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1623c577b8a1SJoseph Chan return 0; 1624c577b8a1SJoseph Chan } 1625c577b8a1SJoseph Chan 1626c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_playback = { 16270aa62aefSHarald Welte .substreams = 2, 1628c577b8a1SJoseph Chan .channels_min = 2, 1629c577b8a1SJoseph Chan .channels_max = 8, 1630c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 1631c577b8a1SJoseph Chan .ops = { 1632c577b8a1SJoseph Chan .open = via_playback_pcm_open, 16330aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 16340aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1635c577b8a1SJoseph Chan }, 1636c577b8a1SJoseph Chan }; 1637c577b8a1SJoseph Chan 1638bc9b5623STakashi Iwai static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 1639c873cc25SLydia Wang .substreams = 2, 1640bc9b5623STakashi Iwai .channels_min = 2, 1641bc9b5623STakashi Iwai .channels_max = 8, 1642bc9b5623STakashi Iwai .nid = 0x10, /* NID to query formats and rates */ 1643bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1644bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1645bc9b5623STakashi Iwai * disable the 24bit format, so far. 1646bc9b5623STakashi Iwai */ 1647bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1648bc9b5623STakashi Iwai .ops = { 1649bc9b5623STakashi Iwai .open = via_playback_pcm_open, 1650c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1651c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1652bc9b5623STakashi Iwai }, 1653bc9b5623STakashi Iwai }; 1654bc9b5623STakashi Iwai 1655c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_capture = { 1656c577b8a1SJoseph Chan .substreams = 2, 1657c577b8a1SJoseph Chan .channels_min = 2, 1658c577b8a1SJoseph Chan .channels_max = 2, 1659c577b8a1SJoseph Chan .nid = 0x15, /* NID to query formats and rates */ 1660c577b8a1SJoseph Chan .ops = { 1661c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1662c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1663c577b8a1SJoseph Chan }, 1664c577b8a1SJoseph Chan }; 1665c577b8a1SJoseph Chan 1666c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_playback = { 1667c577b8a1SJoseph Chan .substreams = 1, 1668c577b8a1SJoseph Chan .channels_min = 2, 1669c577b8a1SJoseph Chan .channels_max = 2, 1670c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1671c577b8a1SJoseph Chan .ops = { 1672c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 16736b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 16749da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 16759da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1676c577b8a1SJoseph Chan }, 1677c577b8a1SJoseph Chan }; 1678c577b8a1SJoseph Chan 1679c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_capture = { 1680c577b8a1SJoseph Chan .substreams = 1, 1681c577b8a1SJoseph Chan .channels_min = 2, 1682c577b8a1SJoseph Chan .channels_max = 2, 1683c577b8a1SJoseph Chan }; 1684c577b8a1SJoseph Chan 1685c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1686c577b8a1SJoseph Chan { 1687c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1688c577b8a1SJoseph Chan int err; 1689c577b8a1SJoseph Chan int i; 1690c577b8a1SJoseph Chan 1691c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1692c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1693c577b8a1SJoseph Chan if (err < 0) 1694c577b8a1SJoseph Chan return err; 1695c577b8a1SJoseph Chan } 1696c577b8a1SJoseph Chan 1697c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1698c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 1699c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1700c577b8a1SJoseph Chan if (err < 0) 1701c577b8a1SJoseph Chan return err; 17029a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 17039a08160bSTakashi Iwai &spec->multiout); 17049a08160bSTakashi Iwai if (err < 0) 17059a08160bSTakashi Iwai return err; 17069a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1707c577b8a1SJoseph Chan } 1708c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1709c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1710c577b8a1SJoseph Chan if (err < 0) 1711c577b8a1SJoseph Chan return err; 1712c577b8a1SJoseph Chan } 171317314379SLydia Wang 171417314379SLydia Wang /* init power states */ 171517314379SLydia Wang set_jack_power_state(codec); 171617314379SLydia Wang analog_low_current_mode(codec, 1); 171717314379SLydia Wang 1718603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1719c577b8a1SJoseph Chan return 0; 1720c577b8a1SJoseph Chan } 1721c577b8a1SJoseph Chan 1722c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1723c577b8a1SJoseph Chan { 1724c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1725c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1726c577b8a1SJoseph Chan 1727c577b8a1SJoseph Chan codec->num_pcms = 1; 1728c577b8a1SJoseph Chan codec->pcm_info = info; 1729c577b8a1SJoseph Chan 1730c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 1731c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback); 1732c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; 1733c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); 1734c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 1735c577b8a1SJoseph Chan 1736c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1737c577b8a1SJoseph Chan spec->multiout.max_channels; 1738c577b8a1SJoseph Chan 1739c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1740c577b8a1SJoseph Chan codec->num_pcms++; 1741c577b8a1SJoseph Chan info++; 1742c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 17437ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1744c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1745c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1746c577b8a1SJoseph Chan *(spec->stream_digital_playback); 1747c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1748c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1749c577b8a1SJoseph Chan } 1750c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1751c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 1752c577b8a1SJoseph Chan *(spec->stream_digital_capture); 1753c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1754c577b8a1SJoseph Chan spec->dig_in_nid; 1755c577b8a1SJoseph Chan } 1756c577b8a1SJoseph Chan } 1757c577b8a1SJoseph Chan 1758c577b8a1SJoseph Chan return 0; 1759c577b8a1SJoseph Chan } 1760c577b8a1SJoseph Chan 1761c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1762c577b8a1SJoseph Chan { 1763c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1764c577b8a1SJoseph Chan 1765c577b8a1SJoseph Chan if (!spec) 1766c577b8a1SJoseph Chan return; 1767c577b8a1SJoseph Chan 1768603c4019STakashi Iwai via_free_kctls(codec); 17691f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1770c577b8a1SJoseph Chan kfree(codec->spec); 1771c577b8a1SJoseph Chan } 1772c577b8a1SJoseph Chan 177369e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 177469e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 177569e52a80SHarald Welte { 1776dcf34c8cSLydia Wang unsigned int present = 0; 177769e52a80SHarald Welte struct via_spec *spec = codec->spec; 177869e52a80SHarald Welte 177969e52a80SHarald Welte present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, 178069e52a80SHarald Welte AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 1781dcf34c8cSLydia Wang 1782dcf34c8cSLydia Wang if (!spec->hp_independent_mode) { 1783dcf34c8cSLydia Wang struct snd_ctl_elem_id id; 1784dcf34c8cSLydia Wang /* auto mute */ 1785dcf34c8cSLydia Wang snd_hda_codec_amp_stereo( 1786dcf34c8cSLydia Wang codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0, 1787dcf34c8cSLydia Wang HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); 1788dcf34c8cSLydia Wang /* notify change */ 1789dcf34c8cSLydia Wang memset(&id, 0, sizeof(id)); 1790dcf34c8cSLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 1791dcf34c8cSLydia Wang strcpy(id.name, "Front Playback Switch"); 1792dcf34c8cSLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 1793dcf34c8cSLydia Wang &id); 1794dcf34c8cSLydia Wang } 179569e52a80SHarald Welte } 179669e52a80SHarald Welte 1797f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */ 1798f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec) 1799f3db423dSLydia Wang { 1800f3db423dSLydia Wang unsigned int hp_present, lineout_present; 1801f3db423dSLydia Wang struct via_spec *spec = codec->spec; 1802f3db423dSLydia Wang 1803f3db423dSLydia Wang if (spec->codec_type != VT1716S) 1804f3db423dSLydia Wang return; 1805f3db423dSLydia Wang 1806f3db423dSLydia Wang lineout_present = snd_hda_codec_read( 1807f3db423dSLydia Wang codec, spec->autocfg.line_out_pins[0], 0, 1808f3db423dSLydia Wang AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 1809f3db423dSLydia Wang 1810f3db423dSLydia Wang /* Mute Mono Out if Line Out is plugged */ 1811f3db423dSLydia Wang if (lineout_present) { 1812f3db423dSLydia Wang snd_hda_codec_amp_stereo( 1813f3db423dSLydia Wang codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); 1814f3db423dSLydia Wang return; 1815f3db423dSLydia Wang } 1816f3db423dSLydia Wang 1817f3db423dSLydia Wang hp_present = snd_hda_codec_read( 1818f3db423dSLydia Wang codec, spec->autocfg.hp_pins[0], 0, 1819f3db423dSLydia Wang AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 1820f3db423dSLydia Wang 1821f3db423dSLydia Wang if (!spec->hp_independent_mode) 1822f3db423dSLydia Wang snd_hda_codec_amp_stereo( 1823f3db423dSLydia Wang codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, 1824f3db423dSLydia Wang hp_present ? HDA_AMP_MUTE : 0); 1825f3db423dSLydia Wang } 1826f3db423dSLydia Wang 182769e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 182869e52a80SHarald Welte { 182969e52a80SHarald Welte unsigned int gpio_data; 183069e52a80SHarald Welte unsigned int vol_counter; 183169e52a80SHarald Welte unsigned int vol; 183269e52a80SHarald Welte unsigned int master_vol; 183369e52a80SHarald Welte 183469e52a80SHarald Welte struct via_spec *spec = codec->spec; 183569e52a80SHarald Welte 183669e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 183769e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 183869e52a80SHarald Welte 183969e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 184069e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 184169e52a80SHarald Welte 184269e52a80SHarald Welte vol = vol_counter & 0x1F; 184369e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 184469e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 184569e52a80SHarald Welte AC_AMP_GET_INPUT); 184669e52a80SHarald Welte 184769e52a80SHarald Welte if (gpio_data == 0x02) { 184869e52a80SHarald Welte /* unmute line out */ 184969e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], 185069e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 185169e52a80SHarald Welte 185269e52a80SHarald Welte if (vol_counter & 0x20) { 185369e52a80SHarald Welte /* decrease volume */ 185469e52a80SHarald Welte if (vol > master_vol) 185569e52a80SHarald Welte vol = master_vol; 185669e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 185769e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 185869e52a80SHarald Welte master_vol-vol); 185969e52a80SHarald Welte } else { 186069e52a80SHarald Welte /* increase volume */ 186169e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 186269e52a80SHarald Welte HDA_AMP_VOLMASK, 186369e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 186469e52a80SHarald Welte (master_vol+vol)); 186569e52a80SHarald Welte } 186669e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 186769e52a80SHarald Welte /* mute line out */ 186869e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 186969e52a80SHarald Welte spec->autocfg.line_out_pins[0], 187069e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 187169e52a80SHarald Welte HDA_AMP_MUTE); 187269e52a80SHarald Welte } 187369e52a80SHarald Welte } 187469e52a80SHarald Welte 1875*25eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */ 1876*25eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec) 1877*25eaba2fSLydia Wang { 1878*25eaba2fSLydia Wang unsigned int hp_present; 1879*25eaba2fSLydia Wang struct via_spec *spec = codec->spec; 1880*25eaba2fSLydia Wang 1881*25eaba2fSLydia Wang if (spec->codec_type != VT2002P) 1882*25eaba2fSLydia Wang return; 1883*25eaba2fSLydia Wang 1884*25eaba2fSLydia Wang hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, 1885*25eaba2fSLydia Wang AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 1886*25eaba2fSLydia Wang 1887*25eaba2fSLydia Wang if (!spec->hp_independent_mode) { 1888*25eaba2fSLydia Wang struct snd_ctl_elem_id id; 1889*25eaba2fSLydia Wang snd_hda_codec_amp_stereo( 1890*25eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0, 1891*25eaba2fSLydia Wang HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); 1892*25eaba2fSLydia Wang /* notify change */ 1893*25eaba2fSLydia Wang memset(&id, 0, sizeof(id)); 1894*25eaba2fSLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 1895*25eaba2fSLydia Wang strcpy(id.name, "Speaker Playback Switch"); 1896*25eaba2fSLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 1897*25eaba2fSLydia Wang &id); 1898*25eaba2fSLydia Wang } 1899*25eaba2fSLydia Wang } 1900*25eaba2fSLydia Wang 1901*25eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */ 1902*25eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec) 1903*25eaba2fSLydia Wang { 1904*25eaba2fSLydia Wang unsigned int hp_present, present = 0; 1905*25eaba2fSLydia Wang struct via_spec *spec = codec->spec; 1906*25eaba2fSLydia Wang int i; 1907*25eaba2fSLydia Wang 1908*25eaba2fSLydia Wang if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) 1909*25eaba2fSLydia Wang return; 1910*25eaba2fSLydia Wang 1911*25eaba2fSLydia Wang hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, 1912*25eaba2fSLydia Wang AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 1913*25eaba2fSLydia Wang 1914*25eaba2fSLydia Wang present = snd_hda_codec_read(codec, spec->autocfg.line_out_pins[0], 0, 1915*25eaba2fSLydia Wang AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 1916*25eaba2fSLydia Wang 1917*25eaba2fSLydia Wang if (!spec->hp_independent_mode) { 1918*25eaba2fSLydia Wang /* Mute Line-Outs */ 1919*25eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 1920*25eaba2fSLydia Wang snd_hda_codec_amp_stereo( 1921*25eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 1922*25eaba2fSLydia Wang HDA_OUTPUT, 0, 1923*25eaba2fSLydia Wang HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); 1924*25eaba2fSLydia Wang if (hp_present) 1925*25eaba2fSLydia Wang present = hp_present; 1926*25eaba2fSLydia Wang } 1927*25eaba2fSLydia Wang /* Speakers */ 1928*25eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 1929*25eaba2fSLydia Wang snd_hda_codec_amp_stereo( 1930*25eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0, 1931*25eaba2fSLydia Wang HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); 1932*25eaba2fSLydia Wang } 1933*25eaba2fSLydia Wang 1934*25eaba2fSLydia Wang 193569e52a80SHarald Welte /* unsolicited event for jack sensing */ 193669e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 193769e52a80SHarald Welte unsigned int res) 193869e52a80SHarald Welte { 193969e52a80SHarald Welte res >>= 26; 1940a34df19aSLydia Wang if (res & VIA_HP_EVENT) 194169e52a80SHarald Welte via_hp_automute(codec); 1942a34df19aSLydia Wang if (res & VIA_GPIO_EVENT) 194369e52a80SHarald Welte via_gpio_control(codec); 1944a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 1945a34df19aSLydia Wang set_jack_power_state(codec); 1946f3db423dSLydia Wang if (res & VIA_MONO_EVENT) 1947f3db423dSLydia Wang via_mono_automute(codec); 1948*25eaba2fSLydia Wang if (res & VIA_SPEAKER_EVENT) 1949*25eaba2fSLydia Wang via_speaker_automute(codec); 1950*25eaba2fSLydia Wang if (res & VIA_BIND_HP_EVENT) 1951*25eaba2fSLydia Wang via_hp_bind_automute(codec); 195269e52a80SHarald Welte } 195369e52a80SHarald Welte 1954c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec) 1955c577b8a1SJoseph Chan { 1956c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 195769e52a80SHarald Welte int i; 195869e52a80SHarald Welte for (i = 0; i < spec->num_iverbs; i++) 195969e52a80SHarald Welte snd_hda_sequence_write(codec, spec->init_verbs[i]); 196069e52a80SHarald Welte 1961518bf3baSLydia Wang spec->codec_type = get_codec_type(codec); 1962518bf3baSLydia Wang if (spec->codec_type == VT1708BCE) 1963518bf3baSLydia Wang spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost 1964518bf3baSLydia Wang same */ 1965f7278fd0SJosepch Chan /* Lydia Add for EAPD enable */ 1966f7278fd0SJosepch Chan if (!spec->dig_in_nid) { /* No Digital In connection */ 196755d1d6c1STakashi Iwai if (spec->dig_in_pin) { 196855d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1969f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 197012b74c80STakashi Iwai PIN_OUT); 197155d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1972f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 1973f7278fd0SJosepch Chan } 197412b74c80STakashi Iwai } else /* enable SPDIF-input pin */ 197512b74c80STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 197612b74c80STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 1977f7278fd0SJosepch Chan 19789da29271STakashi Iwai /* assign slave outs */ 19799da29271STakashi Iwai if (spec->slave_dig_outs[0]) 19809da29271STakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 19815691ec7fSHarald Welte 1982c577b8a1SJoseph Chan return 0; 1983c577b8a1SJoseph Chan } 1984c577b8a1SJoseph Chan 19851f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 19861f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 19871f2e99feSLydia Wang { 19881f2e99feSLydia Wang struct via_spec *spec = codec->spec; 19891f2e99feSLydia Wang vt1708_stop_hp_work(spec); 19901f2e99feSLydia Wang return 0; 19911f2e99feSLydia Wang } 19921f2e99feSLydia Wang #endif 19931f2e99feSLydia Wang 1994cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1995cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 1996cb53c626STakashi Iwai { 1997cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 1998cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 1999cb53c626STakashi Iwai } 2000cb53c626STakashi Iwai #endif 2001cb53c626STakashi Iwai 2002c577b8a1SJoseph Chan /* 2003c577b8a1SJoseph Chan */ 2004c577b8a1SJoseph Chan static struct hda_codec_ops via_patch_ops = { 2005c577b8a1SJoseph Chan .build_controls = via_build_controls, 2006c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 2007c577b8a1SJoseph Chan .init = via_init, 2008c577b8a1SJoseph Chan .free = via_free, 20091f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 20101f2e99feSLydia Wang .suspend = via_suspend, 20111f2e99feSLydia Wang #endif 2012cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2013cb53c626STakashi Iwai .check_power_status = via_check_power_status, 2014cb53c626STakashi Iwai #endif 2015c577b8a1SJoseph Chan }; 2016c577b8a1SJoseph Chan 2017c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */ 2018c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec, 2019c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2020c577b8a1SJoseph Chan { 2021c577b8a1SJoseph Chan int i; 2022c577b8a1SJoseph Chan hda_nid_t nid; 2023c577b8a1SJoseph Chan 2024c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; 2025c577b8a1SJoseph Chan 2026c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 2027c577b8a1SJoseph Chan 2028c577b8a1SJoseph Chan for(i = 0; i < 4; i++) { 2029c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2030c577b8a1SJoseph Chan if (nid) { 2031c577b8a1SJoseph Chan /* config dac list */ 2032c577b8a1SJoseph Chan switch (i) { 2033c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2034c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2035c577b8a1SJoseph Chan break; 2036c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2037c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2038c577b8a1SJoseph Chan break; 2039c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2040fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 2041c577b8a1SJoseph Chan break; 2042c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 2043fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x13; 2044c577b8a1SJoseph Chan break; 2045c577b8a1SJoseph Chan } 2046c577b8a1SJoseph Chan } 2047c577b8a1SJoseph Chan } 2048c577b8a1SJoseph Chan 2049c577b8a1SJoseph Chan return 0; 2050c577b8a1SJoseph Chan } 2051c577b8a1SJoseph Chan 2052c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 2053c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, 2054c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2055c577b8a1SJoseph Chan { 2056c577b8a1SJoseph Chan char name[32]; 2057c577b8a1SJoseph Chan static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 20589645c203SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b}; 2059c577b8a1SJoseph Chan int i, err; 2060c577b8a1SJoseph Chan 2061c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 2062c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2063c577b8a1SJoseph Chan 2064c577b8a1SJoseph Chan if (!nid) 2065c577b8a1SJoseph Chan continue; 2066c577b8a1SJoseph Chan 20679645c203SLydia Wang nid_vol = nid_vols[i]; 2068c577b8a1SJoseph Chan 2069c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 2070c577b8a1SJoseph Chan /* Center/LFE */ 2071c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2072c577b8a1SJoseph Chan "Center Playback Volume", 2073f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2074f7278fd0SJosepch Chan HDA_OUTPUT)); 2075c577b8a1SJoseph Chan if (err < 0) 2076c577b8a1SJoseph Chan return err; 2077c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2078c577b8a1SJoseph Chan "LFE Playback Volume", 2079f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2080f7278fd0SJosepch Chan HDA_OUTPUT)); 2081c577b8a1SJoseph Chan if (err < 0) 2082c577b8a1SJoseph Chan return err; 2083c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2084c577b8a1SJoseph Chan "Center Playback Switch", 2085f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2086f7278fd0SJosepch Chan HDA_OUTPUT)); 2087c577b8a1SJoseph Chan if (err < 0) 2088c577b8a1SJoseph Chan return err; 2089c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2090c577b8a1SJoseph Chan "LFE Playback Switch", 2091f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2092f7278fd0SJosepch Chan HDA_OUTPUT)); 2093c577b8a1SJoseph Chan if (err < 0) 2094c577b8a1SJoseph Chan return err; 2095c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT){ 2096c577b8a1SJoseph Chan /* add control to mixer index 0 */ 2097c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2098c577b8a1SJoseph Chan "Master Front Playback Volume", 20999645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2100f7278fd0SJosepch Chan HDA_INPUT)); 2101c577b8a1SJoseph Chan if (err < 0) 2102c577b8a1SJoseph Chan return err; 2103c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2104c577b8a1SJoseph Chan "Master Front Playback Switch", 21059645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2106f7278fd0SJosepch Chan HDA_INPUT)); 2107c577b8a1SJoseph Chan if (err < 0) 2108c577b8a1SJoseph Chan return err; 2109c577b8a1SJoseph Chan 2110c577b8a1SJoseph Chan /* add control to PW3 */ 2111c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2112c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2113f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2114f7278fd0SJosepch Chan HDA_OUTPUT)); 2115c577b8a1SJoseph Chan if (err < 0) 2116c577b8a1SJoseph Chan return err; 2117c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2118c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2119f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2120f7278fd0SJosepch Chan HDA_OUTPUT)); 2121c577b8a1SJoseph Chan if (err < 0) 2122c577b8a1SJoseph Chan return err; 2123c577b8a1SJoseph Chan } else { 2124c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2125c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2126f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2127f7278fd0SJosepch Chan HDA_OUTPUT)); 2128c577b8a1SJoseph Chan if (err < 0) 2129c577b8a1SJoseph Chan return err; 2130c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2131c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2132f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2133f7278fd0SJosepch Chan HDA_OUTPUT)); 2134c577b8a1SJoseph Chan if (err < 0) 2135c577b8a1SJoseph Chan return err; 2136c577b8a1SJoseph Chan } 2137c577b8a1SJoseph Chan } 2138c577b8a1SJoseph Chan 2139c577b8a1SJoseph Chan return 0; 2140c577b8a1SJoseph Chan } 2141c577b8a1SJoseph Chan 21420aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 21430aa62aefSHarald Welte { 21440aa62aefSHarald Welte int i; 21450aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 21460aa62aefSHarald Welte static const char *texts[] = { "OFF", "ON", NULL}; 21470aa62aefSHarald Welte 21480aa62aefSHarald Welte /* for hp mode select */ 21490aa62aefSHarald Welte i = 0; 21500aa62aefSHarald Welte while (texts[i] != NULL) { 21510aa62aefSHarald Welte imux->items[imux->num_items].label = texts[i]; 21520aa62aefSHarald Welte imux->items[imux->num_items].index = i; 21530aa62aefSHarald Welte imux->num_items++; 21540aa62aefSHarald Welte i++; 21550aa62aefSHarald Welte } 21560aa62aefSHarald Welte 21570aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 21580aa62aefSHarald Welte } 21590aa62aefSHarald Welte 2160c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 2161c577b8a1SJoseph Chan { 2162c577b8a1SJoseph Chan int err; 2163c577b8a1SJoseph Chan 2164c577b8a1SJoseph Chan if (!pin) 2165c577b8a1SJoseph Chan return 0; 2166c577b8a1SJoseph Chan 2167c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ 2168cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 2169c577b8a1SJoseph Chan 2170c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2171c577b8a1SJoseph Chan "Headphone Playback Volume", 2172c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2173c577b8a1SJoseph Chan if (err < 0) 2174c577b8a1SJoseph Chan return err; 2175c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2176c577b8a1SJoseph Chan "Headphone Playback Switch", 2177c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2178c577b8a1SJoseph Chan if (err < 0) 2179c577b8a1SJoseph Chan return err; 2180c577b8a1SJoseph Chan 21810aa62aefSHarald Welte create_hp_imux(spec); 21820aa62aefSHarald Welte 2183c577b8a1SJoseph Chan return 0; 2184c577b8a1SJoseph Chan } 2185c577b8a1SJoseph Chan 2186c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 2187c577b8a1SJoseph Chan static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec, 2188c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2189c577b8a1SJoseph Chan { 2190c577b8a1SJoseph Chan static char *labels[] = { 2191c577b8a1SJoseph Chan "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 2192c577b8a1SJoseph Chan }; 21930aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 2194c577b8a1SJoseph Chan int i, err, idx = 0; 2195c577b8a1SJoseph Chan 2196c577b8a1SJoseph Chan /* for internal loopback recording select */ 2197c577b8a1SJoseph Chan imux->items[imux->num_items].label = "Stereo Mixer"; 2198c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 2199c577b8a1SJoseph Chan imux->num_items++; 2200c577b8a1SJoseph Chan 2201c577b8a1SJoseph Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 2202c577b8a1SJoseph Chan if (!cfg->input_pins[i]) 2203c577b8a1SJoseph Chan continue; 2204c577b8a1SJoseph Chan 2205c577b8a1SJoseph Chan switch (cfg->input_pins[i]) { 2206c577b8a1SJoseph Chan case 0x1d: /* Mic */ 2207c577b8a1SJoseph Chan idx = 2; 2208c577b8a1SJoseph Chan break; 2209c577b8a1SJoseph Chan 2210c577b8a1SJoseph Chan case 0x1e: /* Line In */ 2211c577b8a1SJoseph Chan idx = 3; 2212c577b8a1SJoseph Chan break; 2213c577b8a1SJoseph Chan 2214c577b8a1SJoseph Chan case 0x21: /* Front Mic */ 2215c577b8a1SJoseph Chan idx = 4; 2216c577b8a1SJoseph Chan break; 2217c577b8a1SJoseph Chan 2218c577b8a1SJoseph Chan case 0x24: /* CD */ 2219c577b8a1SJoseph Chan idx = 1; 2220c577b8a1SJoseph Chan break; 2221c577b8a1SJoseph Chan } 22229510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x17); 2223c577b8a1SJoseph Chan if (err < 0) 2224c577b8a1SJoseph Chan return err; 2225c577b8a1SJoseph Chan imux->items[imux->num_items].label = labels[i]; 2226c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 2227c577b8a1SJoseph Chan imux->num_items++; 2228c577b8a1SJoseph Chan } 2229c577b8a1SJoseph Chan return 0; 2230c577b8a1SJoseph Chan } 2231c577b8a1SJoseph Chan 2232cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2233cb53c626STakashi Iwai static struct hda_amp_list vt1708_loopbacks[] = { 2234cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 2235cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 2236cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 2237cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 2238cb53c626STakashi Iwai { } /* end */ 2239cb53c626STakashi Iwai }; 2240cb53c626STakashi Iwai #endif 2241cb53c626STakashi Iwai 224276d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 224376d9b0ddSHarald Welte { 224476d9b0ddSHarald Welte unsigned int def_conf; 224576d9b0ddSHarald Welte unsigned char seqassoc; 224676d9b0ddSHarald Welte 22472f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 224876d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 224976d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 225082ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 225182ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 225276d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 22532f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 225476d9b0ddSHarald Welte } 225576d9b0ddSHarald Welte 225676d9b0ddSHarald Welte return; 225776d9b0ddSHarald Welte } 225876d9b0ddSHarald Welte 22591f2e99feSLydia Wang static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, 22601f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 22611f2e99feSLydia Wang { 22621f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 22631f2e99feSLydia Wang struct via_spec *spec = codec->spec; 22641f2e99feSLydia Wang 22651f2e99feSLydia Wang if (spec->codec_type != VT1708) 22661f2e99feSLydia Wang return 0; 22671f2e99feSLydia Wang spec->vt1708_jack_detectect = 22681f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 22691f2e99feSLydia Wang ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect; 22701f2e99feSLydia Wang return 0; 22711f2e99feSLydia Wang } 22721f2e99feSLydia Wang 22731f2e99feSLydia Wang static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, 22741f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 22751f2e99feSLydia Wang { 22761f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 22771f2e99feSLydia Wang struct via_spec *spec = codec->spec; 22781f2e99feSLydia Wang int change; 22791f2e99feSLydia Wang 22801f2e99feSLydia Wang if (spec->codec_type != VT1708) 22811f2e99feSLydia Wang return 0; 22821f2e99feSLydia Wang spec->vt1708_jack_detectect = ucontrol->value.integer.value[0]; 22831f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 22841f2e99feSLydia Wang == !spec->vt1708_jack_detectect; 22851f2e99feSLydia Wang if (spec->vt1708_jack_detectect) { 22861f2e99feSLydia Wang mute_aa_path(codec, 1); 22871f2e99feSLydia Wang notify_aa_path_ctls(codec); 22881f2e99feSLydia Wang } 22891f2e99feSLydia Wang return change; 22901f2e99feSLydia Wang } 22911f2e99feSLydia Wang 22921f2e99feSLydia Wang static struct snd_kcontrol_new vt1708_jack_detectect[] = { 22931f2e99feSLydia Wang { 22941f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 22951f2e99feSLydia Wang .name = "Jack Detect", 22961f2e99feSLydia Wang .count = 1, 22971f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 22981f2e99feSLydia Wang .get = vt1708_jack_detectect_get, 22991f2e99feSLydia Wang .put = vt1708_jack_detectect_put, 23001f2e99feSLydia Wang }, 23011f2e99feSLydia Wang {} /* end */ 23021f2e99feSLydia Wang }; 23031f2e99feSLydia Wang 2304c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec) 2305c577b8a1SJoseph Chan { 2306c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2307c577b8a1SJoseph Chan int err; 2308c577b8a1SJoseph Chan 230976d9b0ddSHarald Welte /* Add HP and CD pin config connect bit re-config action */ 231076d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 231176d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 231276d9b0ddSHarald Welte 2313c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2314c577b8a1SJoseph Chan if (err < 0) 2315c577b8a1SJoseph Chan return err; 2316c577b8a1SJoseph Chan err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg); 2317c577b8a1SJoseph Chan if (err < 0) 2318c577b8a1SJoseph Chan return err; 2319c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2320c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2321c577b8a1SJoseph Chan 2322c577b8a1SJoseph Chan err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg); 2323c577b8a1SJoseph Chan if (err < 0) 2324c577b8a1SJoseph Chan return err; 2325c577b8a1SJoseph Chan err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2326c577b8a1SJoseph Chan if (err < 0) 2327c577b8a1SJoseph Chan return err; 2328c577b8a1SJoseph Chan err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg); 2329c577b8a1SJoseph Chan if (err < 0) 2330c577b8a1SJoseph Chan return err; 23311f2e99feSLydia Wang /* add jack detect on/off control */ 23321f2e99feSLydia Wang err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect); 23331f2e99feSLydia Wang if (err < 0) 23341f2e99feSLydia Wang return err; 2335c577b8a1SJoseph Chan 2336c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2337c577b8a1SJoseph Chan 23380852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2339c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; 234055d1d6c1STakashi Iwai spec->dig_in_pin = VT1708_DIGIN_PIN; 2341c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2342c577b8a1SJoseph Chan spec->dig_in_nid = VT1708_DIGIN_NID; 2343c577b8a1SJoseph Chan 2344603c4019STakashi Iwai if (spec->kctls.list) 2345603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2346c577b8a1SJoseph Chan 234769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; 2348c577b8a1SJoseph Chan 23490aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 23500aa62aefSHarald Welte 2351f8fdd495SHarald Welte if (spec->hp_mux) 23520aa62aefSHarald Welte spec->mixers[spec->num_mixers++] = via_hp_mixer; 2353c577b8a1SJoseph Chan 23541564b287SLydia Wang spec->mixers[spec->num_mixers++] = via_smart51_mixer; 2355c577b8a1SJoseph Chan return 1; 2356c577b8a1SJoseph Chan } 2357c577b8a1SJoseph Chan 2358c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */ 2359c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec) 2360c577b8a1SJoseph Chan { 2361*25eaba2fSLydia Wang struct via_spec *spec = codec->spec; 2362*25eaba2fSLydia Wang 2363c577b8a1SJoseph Chan via_init(codec); 2364c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2365c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 2366c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 2367*25eaba2fSLydia Wang if (spec->codec_type == VT2002P) { 2368*25eaba2fSLydia Wang via_hp_bind_automute(codec); 2369*25eaba2fSLydia Wang } else { 2370*25eaba2fSLydia Wang via_hp_automute(codec); 2371*25eaba2fSLydia Wang via_speaker_automute(codec); 2372*25eaba2fSLydia Wang } 2373*25eaba2fSLydia Wang 2374c577b8a1SJoseph Chan return 0; 2375c577b8a1SJoseph Chan } 2376c577b8a1SJoseph Chan 23771f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 23781f2e99feSLydia Wang { 23791f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 23801f2e99feSLydia Wang vt1708_hp_work.work); 23811f2e99feSLydia Wang if (spec->codec_type != VT1708) 23821f2e99feSLydia Wang return; 23831f2e99feSLydia Wang /* if jack state toggled */ 23841f2e99feSLydia Wang if (spec->vt1708_hp_present 23851f2e99feSLydia Wang != (snd_hda_codec_read(spec->codec, spec->autocfg.hp_pins[0], 0, 23861f2e99feSLydia Wang AC_VERB_GET_PIN_SENSE, 0) >> 31)) { 23871f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 23881f2e99feSLydia Wang via_hp_automute(spec->codec); 23891f2e99feSLydia Wang } 23901f2e99feSLydia Wang vt1708_start_hp_work(spec); 23911f2e99feSLydia Wang } 23921f2e99feSLydia Wang 2393337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2394337b9d02STakashi Iwai { 2395337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2396337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2397337b9d02STakashi Iwai unsigned int type; 2398337b9d02STakashi Iwai int i, n; 2399337b9d02STakashi Iwai 2400337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2401337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2402337b9d02STakashi Iwai while (nid) { 2403a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 24041c55d521STakashi Iwai if (type == AC_WID_PIN) 24051c55d521STakashi Iwai break; 2406337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2407337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2408337b9d02STakashi Iwai if (n <= 0) 2409337b9d02STakashi Iwai break; 2410337b9d02STakashi Iwai if (n > 1) { 2411337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2412337b9d02STakashi Iwai break; 2413337b9d02STakashi Iwai } 2414337b9d02STakashi Iwai nid = conn[0]; 2415337b9d02STakashi Iwai } 2416337b9d02STakashi Iwai } 24171c55d521STakashi Iwai return 0; 2418337b9d02STakashi Iwai } 2419337b9d02STakashi Iwai 2420c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2421c577b8a1SJoseph Chan { 2422c577b8a1SJoseph Chan struct via_spec *spec; 2423c577b8a1SJoseph Chan int err; 2424c577b8a1SJoseph Chan 2425c577b8a1SJoseph Chan /* create a codec specific record */ 2426eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 2427c577b8a1SJoseph Chan if (spec == NULL) 2428c577b8a1SJoseph Chan return -ENOMEM; 2429c577b8a1SJoseph Chan 2430c577b8a1SJoseph Chan codec->spec = spec; 2431c577b8a1SJoseph Chan 2432c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 2433c577b8a1SJoseph Chan err = vt1708_parse_auto_config(codec); 2434c577b8a1SJoseph Chan if (err < 0) { 2435c577b8a1SJoseph Chan via_free(codec); 2436c577b8a1SJoseph Chan return err; 2437c577b8a1SJoseph Chan } else if (!err) { 2438c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2439c577b8a1SJoseph Chan "from BIOS. Using genenic mode...\n"); 2440c577b8a1SJoseph Chan } 2441c577b8a1SJoseph Chan 2442c577b8a1SJoseph Chan 2443c577b8a1SJoseph Chan spec->stream_name_analog = "VT1708 Analog"; 2444c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1708_pcm_analog_playback; 2445bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2446bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2447bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2448c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1708_pcm_analog_capture; 2449c577b8a1SJoseph Chan 2450c577b8a1SJoseph Chan spec->stream_name_digital = "VT1708 Digital"; 2451c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1708_pcm_digital_playback; 2452c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1708_pcm_digital_capture; 2453c577b8a1SJoseph Chan 2454c577b8a1SJoseph Chan 2455c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2456c577b8a1SJoseph Chan spec->adc_nids = vt1708_adc_nids; 2457c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); 24580f67a611STakashi Iwai get_mux_nids(codec); 2459c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1708_capture_mixer; 2460c577b8a1SJoseph Chan spec->num_mixers++; 2461c577b8a1SJoseph Chan } 2462c577b8a1SJoseph Chan 2463c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2464c577b8a1SJoseph Chan 2465c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 2466cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2467cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 2468cb53c626STakashi Iwai #endif 24691f2e99feSLydia Wang spec->codec = codec; 24701f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2471c577b8a1SJoseph Chan return 0; 2472c577b8a1SJoseph Chan } 2473c577b8a1SJoseph Chan 2474c577b8a1SJoseph Chan /* capture mixer elements */ 2475c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1709_capture_mixer[] = { 2476c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), 2477c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), 2478c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), 2479c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), 2480c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), 2481c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), 2482c577b8a1SJoseph Chan { 2483c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2484c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 2485c577b8a1SJoseph Chan * So call somewhat different.. 2486c577b8a1SJoseph Chan */ 2487c577b8a1SJoseph Chan /* .name = "Capture Source", */ 2488c577b8a1SJoseph Chan .name = "Input Source", 2489c577b8a1SJoseph Chan .count = 1, 2490c577b8a1SJoseph Chan .info = via_mux_enum_info, 2491c577b8a1SJoseph Chan .get = via_mux_enum_get, 2492c577b8a1SJoseph Chan .put = via_mux_enum_put, 2493c577b8a1SJoseph Chan }, 2494c577b8a1SJoseph Chan { } /* end */ 2495c577b8a1SJoseph Chan }; 2496c577b8a1SJoseph Chan 249769e52a80SHarald Welte static struct hda_verb vt1709_uniwill_init_verbs[] = { 2498a34df19aSLydia Wang {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 2499a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 250069e52a80SHarald Welte { } 250169e52a80SHarald Welte }; 250269e52a80SHarald Welte 2503c577b8a1SJoseph Chan /* 2504c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2505c577b8a1SJoseph Chan */ 2506c577b8a1SJoseph Chan static struct hda_verb vt1709_10ch_volume_init_verbs[] = { 2507c577b8a1SJoseph Chan /* 2508c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2509c577b8a1SJoseph Chan */ 2510c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2511c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2512c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2513c577b8a1SJoseph Chan 2514c577b8a1SJoseph Chan 2515f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2516c577b8a1SJoseph Chan * mixer widget 2517c577b8a1SJoseph Chan */ 2518c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2519f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2520f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2521f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2522f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2523f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2524c577b8a1SJoseph Chan 2525c577b8a1SJoseph Chan /* 2526c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2527c577b8a1SJoseph Chan */ 2528c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2529c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2530c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2531c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2532c577b8a1SJoseph Chan 2533c577b8a1SJoseph Chan /* 2534c577b8a1SJoseph Chan * Unmute PW3 and PW4 2535c577b8a1SJoseph Chan */ 2536c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2537c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2538c577b8a1SJoseph Chan 2539c577b8a1SJoseph Chan /* Set input of PW4 as AOW4 */ 2540c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, 2541c577b8a1SJoseph Chan /* PW9 Output enable */ 2542c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2543c577b8a1SJoseph Chan { } 2544c577b8a1SJoseph Chan }; 2545c577b8a1SJoseph Chan 2546c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { 2547c577b8a1SJoseph Chan .substreams = 1, 2548c577b8a1SJoseph Chan .channels_min = 2, 2549c577b8a1SJoseph Chan .channels_max = 10, 2550c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2551c577b8a1SJoseph Chan .ops = { 2552c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2553c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2554c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2555c577b8a1SJoseph Chan }, 2556c577b8a1SJoseph Chan }; 2557c577b8a1SJoseph Chan 2558c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { 2559c577b8a1SJoseph Chan .substreams = 1, 2560c577b8a1SJoseph Chan .channels_min = 2, 2561c577b8a1SJoseph Chan .channels_max = 6, 2562c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2563c577b8a1SJoseph Chan .ops = { 2564c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2565c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2566c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2567c577b8a1SJoseph Chan }, 2568c577b8a1SJoseph Chan }; 2569c577b8a1SJoseph Chan 2570c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_analog_capture = { 2571c577b8a1SJoseph Chan .substreams = 2, 2572c577b8a1SJoseph Chan .channels_min = 2, 2573c577b8a1SJoseph Chan .channels_max = 2, 2574c577b8a1SJoseph Chan .nid = 0x14, /* NID to query formats and rates */ 2575c577b8a1SJoseph Chan .ops = { 2576c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 2577c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 2578c577b8a1SJoseph Chan }, 2579c577b8a1SJoseph Chan }; 2580c577b8a1SJoseph Chan 2581c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_playback = { 2582c577b8a1SJoseph Chan .substreams = 1, 2583c577b8a1SJoseph Chan .channels_min = 2, 2584c577b8a1SJoseph Chan .channels_max = 2, 2585c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 2586c577b8a1SJoseph Chan .ops = { 2587c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 2588c577b8a1SJoseph Chan .close = via_dig_playback_pcm_close 2589c577b8a1SJoseph Chan }, 2590c577b8a1SJoseph Chan }; 2591c577b8a1SJoseph Chan 2592c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_capture = { 2593c577b8a1SJoseph Chan .substreams = 1, 2594c577b8a1SJoseph Chan .channels_min = 2, 2595c577b8a1SJoseph Chan .channels_max = 2, 2596c577b8a1SJoseph Chan }; 2597c577b8a1SJoseph Chan 2598c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec, 2599c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2600c577b8a1SJoseph Chan { 2601c577b8a1SJoseph Chan int i; 2602c577b8a1SJoseph Chan hda_nid_t nid; 2603c577b8a1SJoseph Chan 2604c577b8a1SJoseph Chan if (cfg->line_outs == 4) /* 10 channels */ 2605c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */ 2606c577b8a1SJoseph Chan else if (cfg->line_outs == 3) /* 6 channels */ 2607c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */ 2608c577b8a1SJoseph Chan 2609c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 2610c577b8a1SJoseph Chan 2611c577b8a1SJoseph Chan if (cfg->line_outs == 4) { /* 10 channels */ 2612c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 2613c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2614c577b8a1SJoseph Chan if (nid) { 2615c577b8a1SJoseph Chan /* config dac list */ 2616c577b8a1SJoseph Chan switch (i) { 2617c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2618c577b8a1SJoseph Chan /* AOW0 */ 2619c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2620c577b8a1SJoseph Chan break; 2621c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2622c577b8a1SJoseph Chan /* AOW2 */ 2623c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2624c577b8a1SJoseph Chan break; 2625c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2626c577b8a1SJoseph Chan /* AOW3 */ 2627fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 2628c577b8a1SJoseph Chan break; 2629c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 2630c577b8a1SJoseph Chan /* AOW1 */ 2631fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x27; 2632c577b8a1SJoseph Chan break; 2633c577b8a1SJoseph Chan default: 2634c577b8a1SJoseph Chan break; 2635c577b8a1SJoseph Chan } 2636c577b8a1SJoseph Chan } 2637c577b8a1SJoseph Chan } 2638c577b8a1SJoseph Chan spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ 2639c577b8a1SJoseph Chan 2640c577b8a1SJoseph Chan } else if (cfg->line_outs == 3) { /* 6 channels */ 2641c577b8a1SJoseph Chan for(i = 0; i < cfg->line_outs; i++) { 2642c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2643c577b8a1SJoseph Chan if (nid) { 2644c577b8a1SJoseph Chan /* config dac list */ 2645c577b8a1SJoseph Chan switch(i) { 2646c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2647c577b8a1SJoseph Chan /* AOW0 */ 2648c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2649c577b8a1SJoseph Chan break; 2650c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2651c577b8a1SJoseph Chan /* AOW2 */ 2652c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2653c577b8a1SJoseph Chan break; 2654c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2655c577b8a1SJoseph Chan /* AOW1 */ 2656c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x11; 2657c577b8a1SJoseph Chan break; 2658c577b8a1SJoseph Chan default: 2659c577b8a1SJoseph Chan break; 2660c577b8a1SJoseph Chan } 2661c577b8a1SJoseph Chan } 2662c577b8a1SJoseph Chan } 2663c577b8a1SJoseph Chan } 2664c577b8a1SJoseph Chan 2665c577b8a1SJoseph Chan return 0; 2666c577b8a1SJoseph Chan } 2667c577b8a1SJoseph Chan 2668c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 2669c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, 2670c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2671c577b8a1SJoseph Chan { 2672c577b8a1SJoseph Chan char name[32]; 2673c577b8a1SJoseph Chan static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 26744483a2f5SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29}; 2675c577b8a1SJoseph Chan int i, err; 2676c577b8a1SJoseph Chan 2677c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 2678c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2679c577b8a1SJoseph Chan 2680c577b8a1SJoseph Chan if (!nid) 2681c577b8a1SJoseph Chan continue; 2682c577b8a1SJoseph Chan 26834483a2f5SLydia Wang nid_vol = nid_vols[i]; 26844483a2f5SLydia Wang 2685c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 2686c577b8a1SJoseph Chan /* Center/LFE */ 2687c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2688c577b8a1SJoseph Chan "Center Playback Volume", 26894483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2690f7278fd0SJosepch Chan HDA_OUTPUT)); 2691c577b8a1SJoseph Chan if (err < 0) 2692c577b8a1SJoseph Chan return err; 2693c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2694c577b8a1SJoseph Chan "LFE Playback Volume", 26954483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2696f7278fd0SJosepch Chan HDA_OUTPUT)); 2697c577b8a1SJoseph Chan if (err < 0) 2698c577b8a1SJoseph Chan return err; 2699c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2700c577b8a1SJoseph Chan "Center Playback Switch", 27014483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2702f7278fd0SJosepch Chan HDA_OUTPUT)); 2703c577b8a1SJoseph Chan if (err < 0) 2704c577b8a1SJoseph Chan return err; 2705c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2706c577b8a1SJoseph Chan "LFE Playback Switch", 27074483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2708f7278fd0SJosepch Chan HDA_OUTPUT)); 2709c577b8a1SJoseph Chan if (err < 0) 2710c577b8a1SJoseph Chan return err; 2711c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT){ 27124483a2f5SLydia Wang /* ADD control to mixer index 0 */ 2713c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2714c577b8a1SJoseph Chan "Master Front Playback Volume", 27154483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2716f7278fd0SJosepch Chan HDA_INPUT)); 2717c577b8a1SJoseph Chan if (err < 0) 2718c577b8a1SJoseph Chan return err; 2719c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2720c577b8a1SJoseph Chan "Master Front Playback Switch", 27214483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2722f7278fd0SJosepch Chan HDA_INPUT)); 2723c577b8a1SJoseph Chan if (err < 0) 2724c577b8a1SJoseph Chan return err; 2725c577b8a1SJoseph Chan 2726c577b8a1SJoseph Chan /* add control to PW3 */ 2727c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2728c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2729f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2730f7278fd0SJosepch Chan HDA_OUTPUT)); 2731c577b8a1SJoseph Chan if (err < 0) 2732c577b8a1SJoseph Chan return err; 2733c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2734c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2735f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2736f7278fd0SJosepch Chan HDA_OUTPUT)); 2737c577b8a1SJoseph Chan if (err < 0) 2738c577b8a1SJoseph Chan return err; 2739c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SURROUND) { 2740c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2741c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 27424483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2743f7278fd0SJosepch Chan HDA_OUTPUT)); 2744c577b8a1SJoseph Chan if (err < 0) 2745c577b8a1SJoseph Chan return err; 2746c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2747c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 27484483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2749f7278fd0SJosepch Chan HDA_OUTPUT)); 2750c577b8a1SJoseph Chan if (err < 0) 2751c577b8a1SJoseph Chan return err; 2752c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SIDE) { 2753c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2754c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 27554483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2756f7278fd0SJosepch Chan HDA_OUTPUT)); 2757c577b8a1SJoseph Chan if (err < 0) 2758c577b8a1SJoseph Chan return err; 2759c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2760c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 27614483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2762f7278fd0SJosepch Chan HDA_OUTPUT)); 2763c577b8a1SJoseph Chan if (err < 0) 2764c577b8a1SJoseph Chan return err; 2765c577b8a1SJoseph Chan } 2766c577b8a1SJoseph Chan } 2767c577b8a1SJoseph Chan 2768c577b8a1SJoseph Chan return 0; 2769c577b8a1SJoseph Chan } 2770c577b8a1SJoseph Chan 2771c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 2772c577b8a1SJoseph Chan { 2773c577b8a1SJoseph Chan int err; 2774c577b8a1SJoseph Chan 2775c577b8a1SJoseph Chan if (!pin) 2776c577b8a1SJoseph Chan return 0; 2777c577b8a1SJoseph Chan 2778c577b8a1SJoseph Chan if (spec->multiout.num_dacs == 5) /* 10 channels */ 2779c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1709_HP_DAC_NID; 2780c577b8a1SJoseph Chan else if (spec->multiout.num_dacs == 3) /* 6 channels */ 2781c577b8a1SJoseph Chan spec->multiout.hp_nid = 0; 2782cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 2783c577b8a1SJoseph Chan 2784c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2785c577b8a1SJoseph Chan "Headphone Playback Volume", 2786c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2787c577b8a1SJoseph Chan if (err < 0) 2788c577b8a1SJoseph Chan return err; 2789c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2790c577b8a1SJoseph Chan "Headphone Playback Switch", 2791c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2792c577b8a1SJoseph Chan if (err < 0) 2793c577b8a1SJoseph Chan return err; 2794c577b8a1SJoseph Chan 2795c577b8a1SJoseph Chan return 0; 2796c577b8a1SJoseph Chan } 2797c577b8a1SJoseph Chan 2798c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 2799c577b8a1SJoseph Chan static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec, 2800c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2801c577b8a1SJoseph Chan { 2802c577b8a1SJoseph Chan static char *labels[] = { 2803c577b8a1SJoseph Chan "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 2804c577b8a1SJoseph Chan }; 28050aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 2806c577b8a1SJoseph Chan int i, err, idx = 0; 2807c577b8a1SJoseph Chan 2808c577b8a1SJoseph Chan /* for internal loopback recording select */ 2809c577b8a1SJoseph Chan imux->items[imux->num_items].label = "Stereo Mixer"; 2810c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 2811c577b8a1SJoseph Chan imux->num_items++; 2812c577b8a1SJoseph Chan 2813c577b8a1SJoseph Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 2814c577b8a1SJoseph Chan if (!cfg->input_pins[i]) 2815c577b8a1SJoseph Chan continue; 2816c577b8a1SJoseph Chan 2817c577b8a1SJoseph Chan switch (cfg->input_pins[i]) { 2818c577b8a1SJoseph Chan case 0x1d: /* Mic */ 2819c577b8a1SJoseph Chan idx = 2; 2820c577b8a1SJoseph Chan break; 2821c577b8a1SJoseph Chan 2822c577b8a1SJoseph Chan case 0x1e: /* Line In */ 2823c577b8a1SJoseph Chan idx = 3; 2824c577b8a1SJoseph Chan break; 2825c577b8a1SJoseph Chan 2826c577b8a1SJoseph Chan case 0x21: /* Front Mic */ 2827c577b8a1SJoseph Chan idx = 4; 2828c577b8a1SJoseph Chan break; 2829c577b8a1SJoseph Chan 2830c577b8a1SJoseph Chan case 0x23: /* CD */ 2831c577b8a1SJoseph Chan idx = 1; 2832c577b8a1SJoseph Chan break; 2833c577b8a1SJoseph Chan } 28349510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x18); 2835c577b8a1SJoseph Chan if (err < 0) 2836c577b8a1SJoseph Chan return err; 2837c577b8a1SJoseph Chan imux->items[imux->num_items].label = labels[i]; 2838c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 2839c577b8a1SJoseph Chan imux->num_items++; 2840c577b8a1SJoseph Chan } 2841c577b8a1SJoseph Chan return 0; 2842c577b8a1SJoseph Chan } 2843c577b8a1SJoseph Chan 2844c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec) 2845c577b8a1SJoseph Chan { 2846c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2847c577b8a1SJoseph Chan int err; 2848c577b8a1SJoseph Chan 2849c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2850c577b8a1SJoseph Chan if (err < 0) 2851c577b8a1SJoseph Chan return err; 2852c577b8a1SJoseph Chan err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg); 2853c577b8a1SJoseph Chan if (err < 0) 2854c577b8a1SJoseph Chan return err; 2855c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2856c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2857c577b8a1SJoseph Chan 2858c577b8a1SJoseph Chan err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg); 2859c577b8a1SJoseph Chan if (err < 0) 2860c577b8a1SJoseph Chan return err; 2861c577b8a1SJoseph Chan err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2862c577b8a1SJoseph Chan if (err < 0) 2863c577b8a1SJoseph Chan return err; 2864c577b8a1SJoseph Chan err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg); 2865c577b8a1SJoseph Chan if (err < 0) 2866c577b8a1SJoseph Chan return err; 2867c577b8a1SJoseph Chan 2868c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2869c577b8a1SJoseph Chan 28700852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2871c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; 287255d1d6c1STakashi Iwai spec->dig_in_pin = VT1709_DIGIN_PIN; 2873c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2874c577b8a1SJoseph Chan spec->dig_in_nid = VT1709_DIGIN_NID; 2875c577b8a1SJoseph Chan 2876603c4019STakashi Iwai if (spec->kctls.list) 2877603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2878c577b8a1SJoseph Chan 28790aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 2880c577b8a1SJoseph Chan 2881f8fdd495SHarald Welte if (spec->hp_mux) 2882f8fdd495SHarald Welte spec->mixers[spec->num_mixers++] = via_hp_mixer; 2883f8fdd495SHarald Welte 28841564b287SLydia Wang spec->mixers[spec->num_mixers++] = via_smart51_mixer; 2885c577b8a1SJoseph Chan return 1; 2886c577b8a1SJoseph Chan } 2887c577b8a1SJoseph Chan 2888cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2889cb53c626STakashi Iwai static struct hda_amp_list vt1709_loopbacks[] = { 2890cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 2891cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 2892cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 2893cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 2894cb53c626STakashi Iwai { } /* end */ 2895cb53c626STakashi Iwai }; 2896cb53c626STakashi Iwai #endif 2897cb53c626STakashi Iwai 2898c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 2899c577b8a1SJoseph Chan { 2900c577b8a1SJoseph Chan struct via_spec *spec; 2901c577b8a1SJoseph Chan int err; 2902c577b8a1SJoseph Chan 2903c577b8a1SJoseph Chan /* create a codec specific record */ 2904eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 2905c577b8a1SJoseph Chan if (spec == NULL) 2906c577b8a1SJoseph Chan return -ENOMEM; 2907c577b8a1SJoseph Chan 2908c577b8a1SJoseph Chan codec->spec = spec; 2909c577b8a1SJoseph Chan 2910c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2911c577b8a1SJoseph Chan if (err < 0) { 2912c577b8a1SJoseph Chan via_free(codec); 2913c577b8a1SJoseph Chan return err; 2914c577b8a1SJoseph Chan } else if (!err) { 2915c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2916c577b8a1SJoseph Chan "Using genenic mode...\n"); 2917c577b8a1SJoseph Chan } 2918c577b8a1SJoseph Chan 291969e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; 292069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2921c577b8a1SJoseph Chan 2922c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 2923c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; 2924c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 2925c577b8a1SJoseph Chan 2926c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 2927c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 2928c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 2929c577b8a1SJoseph Chan 2930c577b8a1SJoseph Chan 2931c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2932c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 2933c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 2934337b9d02STakashi Iwai get_mux_nids(codec); 2935c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2936c577b8a1SJoseph Chan spec->num_mixers++; 2937c577b8a1SJoseph Chan } 2938c577b8a1SJoseph Chan 2939c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2940c577b8a1SJoseph Chan 2941c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 294269e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2943cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2944cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2945cb53c626STakashi Iwai #endif 2946c577b8a1SJoseph Chan 2947c577b8a1SJoseph Chan return 0; 2948c577b8a1SJoseph Chan } 2949c577b8a1SJoseph Chan /* 2950c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2951c577b8a1SJoseph Chan */ 2952c577b8a1SJoseph Chan static struct hda_verb vt1709_6ch_volume_init_verbs[] = { 2953c577b8a1SJoseph Chan /* 2954c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2955c577b8a1SJoseph Chan */ 2956c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2957c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2958c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2959c577b8a1SJoseph Chan 2960c577b8a1SJoseph Chan 2961c577b8a1SJoseph Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2962c577b8a1SJoseph Chan * mixer widget 2963c577b8a1SJoseph Chan */ 2964c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2965c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2966c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2967c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2968c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2969c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2970c577b8a1SJoseph Chan 2971c577b8a1SJoseph Chan /* 2972c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2973c577b8a1SJoseph Chan */ 2974c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2975c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2976c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2977c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2978c577b8a1SJoseph Chan 2979c577b8a1SJoseph Chan /* 2980c577b8a1SJoseph Chan * Unmute PW3 and PW4 2981c577b8a1SJoseph Chan */ 2982c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2983c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2984c577b8a1SJoseph Chan 2985c577b8a1SJoseph Chan /* Set input of PW4 as MW0 */ 2986c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2987c577b8a1SJoseph Chan /* PW9 Output enable */ 2988c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2989c577b8a1SJoseph Chan { } 2990c577b8a1SJoseph Chan }; 2991c577b8a1SJoseph Chan 2992c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 2993c577b8a1SJoseph Chan { 2994c577b8a1SJoseph Chan struct via_spec *spec; 2995c577b8a1SJoseph Chan int err; 2996c577b8a1SJoseph Chan 2997c577b8a1SJoseph Chan /* create a codec specific record */ 2998eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 2999c577b8a1SJoseph Chan if (spec == NULL) 3000c577b8a1SJoseph Chan return -ENOMEM; 3001c577b8a1SJoseph Chan 3002c577b8a1SJoseph Chan codec->spec = spec; 3003c577b8a1SJoseph Chan 3004c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 3005c577b8a1SJoseph Chan if (err < 0) { 3006c577b8a1SJoseph Chan via_free(codec); 3007c577b8a1SJoseph Chan return err; 3008c577b8a1SJoseph Chan } else if (!err) { 3009c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 3010c577b8a1SJoseph Chan "Using genenic mode...\n"); 3011c577b8a1SJoseph Chan } 3012c577b8a1SJoseph Chan 301369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; 301469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 3015c577b8a1SJoseph Chan 3016c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 3017c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; 3018c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 3019c577b8a1SJoseph Chan 3020c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 3021c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 3022c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 3023c577b8a1SJoseph Chan 3024c577b8a1SJoseph Chan 3025c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 3026c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 3027c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 3028337b9d02STakashi Iwai get_mux_nids(codec); 3029c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 3030c577b8a1SJoseph Chan spec->num_mixers++; 3031c577b8a1SJoseph Chan } 3032c577b8a1SJoseph Chan 3033c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 3034c577b8a1SJoseph Chan 3035c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 303669e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3037cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 3038cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 3039cb53c626STakashi Iwai #endif 3040f7278fd0SJosepch Chan return 0; 3041f7278fd0SJosepch Chan } 3042f7278fd0SJosepch Chan 3043f7278fd0SJosepch Chan /* capture mixer elements */ 3044f7278fd0SJosepch Chan static struct snd_kcontrol_new vt1708B_capture_mixer[] = { 3045f7278fd0SJosepch Chan HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3046f7278fd0SJosepch Chan HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3047f7278fd0SJosepch Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3048f7278fd0SJosepch Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 3049f7278fd0SJosepch Chan { 3050f7278fd0SJosepch Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3051f7278fd0SJosepch Chan /* The multiple "Capture Source" controls confuse alsamixer 3052f7278fd0SJosepch Chan * So call somewhat different.. 3053f7278fd0SJosepch Chan */ 3054f7278fd0SJosepch Chan /* .name = "Capture Source", */ 3055f7278fd0SJosepch Chan .name = "Input Source", 3056f7278fd0SJosepch Chan .count = 1, 3057f7278fd0SJosepch Chan .info = via_mux_enum_info, 3058f7278fd0SJosepch Chan .get = via_mux_enum_get, 3059f7278fd0SJosepch Chan .put = via_mux_enum_put, 3060f7278fd0SJosepch Chan }, 3061f7278fd0SJosepch Chan { } /* end */ 3062f7278fd0SJosepch Chan }; 3063f7278fd0SJosepch Chan /* 3064f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 3065f7278fd0SJosepch Chan */ 3066f7278fd0SJosepch Chan static struct hda_verb vt1708B_8ch_volume_init_verbs[] = { 3067f7278fd0SJosepch Chan /* 3068f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 3069f7278fd0SJosepch Chan */ 3070f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3071f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3072f7278fd0SJosepch Chan 3073f7278fd0SJosepch Chan 3074f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3075f7278fd0SJosepch Chan * mixer widget 3076f7278fd0SJosepch Chan */ 3077f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3078f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3079f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3080f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3081f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3082f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3083f7278fd0SJosepch Chan 3084f7278fd0SJosepch Chan /* 3085f7278fd0SJosepch Chan * Set up output mixers 3086f7278fd0SJosepch Chan */ 3087f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 3088f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3089f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3090f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3091f7278fd0SJosepch Chan 3092f7278fd0SJosepch Chan /* Setup default input to PW4 */ 3093f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, 3094f7278fd0SJosepch Chan /* PW9 Output enable */ 3095f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3096f7278fd0SJosepch Chan /* PW10 Input enable */ 3097f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 3098f7278fd0SJosepch Chan { } 3099f7278fd0SJosepch Chan }; 3100f7278fd0SJosepch Chan 3101f7278fd0SJosepch Chan static struct hda_verb vt1708B_4ch_volume_init_verbs[] = { 3102f7278fd0SJosepch Chan /* 3103f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 3104f7278fd0SJosepch Chan */ 3105f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3106f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3107f7278fd0SJosepch Chan 3108f7278fd0SJosepch Chan 3109f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3110f7278fd0SJosepch Chan * mixer widget 3111f7278fd0SJosepch Chan */ 3112f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3113f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3114f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3115f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3116f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3117f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3118f7278fd0SJosepch Chan 3119f7278fd0SJosepch Chan /* 3120f7278fd0SJosepch Chan * Set up output mixers 3121f7278fd0SJosepch Chan */ 3122f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 3123f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3124f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3125f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3126f7278fd0SJosepch Chan 3127f7278fd0SJosepch Chan /* Setup default input of PW4 to MW0 */ 3128f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 3129f7278fd0SJosepch Chan /* PW9 Output enable */ 3130f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3131f7278fd0SJosepch Chan /* PW10 Input enable */ 3132f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 3133f7278fd0SJosepch Chan { } 3134f7278fd0SJosepch Chan }; 3135f7278fd0SJosepch Chan 313669e52a80SHarald Welte static struct hda_verb vt1708B_uniwill_init_verbs[] = { 3137a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3138a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3139a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3140a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3141a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3142a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3143a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3144a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3145a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 314669e52a80SHarald Welte { } 314769e52a80SHarald Welte }; 314869e52a80SHarald Welte 314917314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo, 315017314379SLydia Wang struct hda_codec *codec, 315117314379SLydia Wang struct snd_pcm_substream *substream) 315217314379SLydia Wang { 315317314379SLydia Wang int idle = substream->pstr->substream_opened == 1 315417314379SLydia Wang && substream->ref_count == 0; 315517314379SLydia Wang 315617314379SLydia Wang analog_low_current_mode(codec, idle); 315717314379SLydia Wang return 0; 315817314379SLydia Wang } 315917314379SLydia Wang 3160f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { 31610aa62aefSHarald Welte .substreams = 2, 3162f7278fd0SJosepch Chan .channels_min = 2, 3163f7278fd0SJosepch Chan .channels_max = 8, 3164f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 3165f7278fd0SJosepch Chan .ops = { 3166f7278fd0SJosepch Chan .open = via_playback_pcm_open, 31670aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 316817314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 316917314379SLydia Wang .close = via_pcm_open_close 3170f7278fd0SJosepch Chan }, 3171f7278fd0SJosepch Chan }; 3172f7278fd0SJosepch Chan 3173f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { 31740aa62aefSHarald Welte .substreams = 2, 3175f7278fd0SJosepch Chan .channels_min = 2, 3176f7278fd0SJosepch Chan .channels_max = 4, 3177f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 3178f7278fd0SJosepch Chan .ops = { 3179f7278fd0SJosepch Chan .open = via_playback_pcm_open, 31800aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 31810aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 3182f7278fd0SJosepch Chan }, 3183f7278fd0SJosepch Chan }; 3184f7278fd0SJosepch Chan 3185f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_analog_capture = { 3186f7278fd0SJosepch Chan .substreams = 2, 3187f7278fd0SJosepch Chan .channels_min = 2, 3188f7278fd0SJosepch Chan .channels_max = 2, 3189f7278fd0SJosepch Chan .nid = 0x13, /* NID to query formats and rates */ 3190f7278fd0SJosepch Chan .ops = { 319117314379SLydia Wang .open = via_pcm_open_close, 3192f7278fd0SJosepch Chan .prepare = via_capture_pcm_prepare, 319317314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 319417314379SLydia Wang .close = via_pcm_open_close 3195f7278fd0SJosepch Chan }, 3196f7278fd0SJosepch Chan }; 3197f7278fd0SJosepch Chan 3198f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_playback = { 3199f7278fd0SJosepch Chan .substreams = 1, 3200f7278fd0SJosepch Chan .channels_min = 2, 3201f7278fd0SJosepch Chan .channels_max = 2, 3202f7278fd0SJosepch Chan /* NID is set in via_build_pcms */ 3203f7278fd0SJosepch Chan .ops = { 3204f7278fd0SJosepch Chan .open = via_dig_playback_pcm_open, 3205f7278fd0SJosepch Chan .close = via_dig_playback_pcm_close, 32069da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 32079da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3208f7278fd0SJosepch Chan }, 3209f7278fd0SJosepch Chan }; 3210f7278fd0SJosepch Chan 3211f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_capture = { 3212f7278fd0SJosepch Chan .substreams = 1, 3213f7278fd0SJosepch Chan .channels_min = 2, 3214f7278fd0SJosepch Chan .channels_max = 2, 3215f7278fd0SJosepch Chan }; 3216f7278fd0SJosepch Chan 3217f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */ 3218f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, 3219f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3220f7278fd0SJosepch Chan { 3221f7278fd0SJosepch Chan int i; 3222f7278fd0SJosepch Chan hda_nid_t nid; 3223f7278fd0SJosepch Chan 3224f7278fd0SJosepch Chan spec->multiout.num_dacs = cfg->line_outs; 3225f7278fd0SJosepch Chan 3226f7278fd0SJosepch Chan spec->multiout.dac_nids = spec->private_dac_nids; 3227f7278fd0SJosepch Chan 3228f7278fd0SJosepch Chan for (i = 0; i < 4; i++) { 3229f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 3230f7278fd0SJosepch Chan if (nid) { 3231f7278fd0SJosepch Chan /* config dac list */ 3232f7278fd0SJosepch Chan switch (i) { 3233f7278fd0SJosepch Chan case AUTO_SEQ_FRONT: 3234f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x10; 3235f7278fd0SJosepch Chan break; 3236f7278fd0SJosepch Chan case AUTO_SEQ_CENLFE: 3237f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x24; 3238f7278fd0SJosepch Chan break; 3239f7278fd0SJosepch Chan case AUTO_SEQ_SURROUND: 3240fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 3241f7278fd0SJosepch Chan break; 3242f7278fd0SJosepch Chan case AUTO_SEQ_SIDE: 3243fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x25; 3244f7278fd0SJosepch Chan break; 3245f7278fd0SJosepch Chan } 3246f7278fd0SJosepch Chan } 3247f7278fd0SJosepch Chan } 3248f7278fd0SJosepch Chan 3249f7278fd0SJosepch Chan return 0; 3250f7278fd0SJosepch Chan } 3251f7278fd0SJosepch Chan 3252f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */ 3253f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, 3254f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3255f7278fd0SJosepch Chan { 3256f7278fd0SJosepch Chan char name[32]; 3257f7278fd0SJosepch Chan static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 3258fb4cb772SHarald Welte hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27}; 3259f7278fd0SJosepch Chan hda_nid_t nid, nid_vol = 0; 3260f7278fd0SJosepch Chan int i, err; 3261f7278fd0SJosepch Chan 3262f7278fd0SJosepch Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3263f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 3264f7278fd0SJosepch Chan 3265f7278fd0SJosepch Chan if (!nid) 3266f7278fd0SJosepch Chan continue; 3267f7278fd0SJosepch Chan 3268f7278fd0SJosepch Chan nid_vol = nid_vols[i]; 3269f7278fd0SJosepch Chan 3270f7278fd0SJosepch Chan if (i == AUTO_SEQ_CENLFE) { 3271f7278fd0SJosepch Chan /* Center/LFE */ 3272f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3273f7278fd0SJosepch Chan "Center Playback Volume", 3274f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3275f7278fd0SJosepch Chan HDA_OUTPUT)); 3276f7278fd0SJosepch Chan if (err < 0) 3277f7278fd0SJosepch Chan return err; 3278f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3279f7278fd0SJosepch Chan "LFE Playback Volume", 3280f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3281f7278fd0SJosepch Chan HDA_OUTPUT)); 3282f7278fd0SJosepch Chan if (err < 0) 3283f7278fd0SJosepch Chan return err; 3284f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3285f7278fd0SJosepch Chan "Center Playback Switch", 3286f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3287f7278fd0SJosepch Chan HDA_OUTPUT)); 3288f7278fd0SJosepch Chan if (err < 0) 3289f7278fd0SJosepch Chan return err; 3290f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3291f7278fd0SJosepch Chan "LFE Playback Switch", 3292f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3293f7278fd0SJosepch Chan HDA_OUTPUT)); 3294f7278fd0SJosepch Chan if (err < 0) 3295f7278fd0SJosepch Chan return err; 3296f7278fd0SJosepch Chan } else if (i == AUTO_SEQ_FRONT) { 3297f7278fd0SJosepch Chan /* add control to mixer index 0 */ 3298f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3299f7278fd0SJosepch Chan "Master Front Playback Volume", 3300f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3301f7278fd0SJosepch Chan HDA_INPUT)); 3302f7278fd0SJosepch Chan if (err < 0) 3303f7278fd0SJosepch Chan return err; 3304f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3305f7278fd0SJosepch Chan "Master Front Playback Switch", 3306f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3307f7278fd0SJosepch Chan HDA_INPUT)); 3308f7278fd0SJosepch Chan if (err < 0) 3309f7278fd0SJosepch Chan return err; 3310f7278fd0SJosepch Chan 3311f7278fd0SJosepch Chan /* add control to PW3 */ 3312f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3313f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3314f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3315f7278fd0SJosepch Chan HDA_OUTPUT)); 3316f7278fd0SJosepch Chan if (err < 0) 3317f7278fd0SJosepch Chan return err; 3318f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3319f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3320f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3321f7278fd0SJosepch Chan HDA_OUTPUT)); 3322f7278fd0SJosepch Chan if (err < 0) 3323f7278fd0SJosepch Chan return err; 3324f7278fd0SJosepch Chan } else { 3325f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3326f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3327f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3328f7278fd0SJosepch Chan HDA_OUTPUT)); 3329f7278fd0SJosepch Chan if (err < 0) 3330f7278fd0SJosepch Chan return err; 3331f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3332f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3333f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3334f7278fd0SJosepch Chan HDA_OUTPUT)); 3335f7278fd0SJosepch Chan if (err < 0) 3336f7278fd0SJosepch Chan return err; 3337f7278fd0SJosepch Chan } 3338f7278fd0SJosepch Chan } 3339f7278fd0SJosepch Chan 3340f7278fd0SJosepch Chan return 0; 3341f7278fd0SJosepch Chan } 3342f7278fd0SJosepch Chan 3343f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3344f7278fd0SJosepch Chan { 3345f7278fd0SJosepch Chan int err; 3346f7278fd0SJosepch Chan 3347f7278fd0SJosepch Chan if (!pin) 3348f7278fd0SJosepch Chan return 0; 3349f7278fd0SJosepch Chan 3350f7278fd0SJosepch Chan spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ 3351cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3352f7278fd0SJosepch Chan 3353f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3354f7278fd0SJosepch Chan "Headphone Playback Volume", 3355f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3356f7278fd0SJosepch Chan if (err < 0) 3357f7278fd0SJosepch Chan return err; 3358f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3359f7278fd0SJosepch Chan "Headphone Playback Switch", 3360f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3361f7278fd0SJosepch Chan if (err < 0) 3362f7278fd0SJosepch Chan return err; 3363f7278fd0SJosepch Chan 33640aa62aefSHarald Welte create_hp_imux(spec); 33650aa62aefSHarald Welte 3366f7278fd0SJosepch Chan return 0; 3367f7278fd0SJosepch Chan } 3368f7278fd0SJosepch Chan 3369f7278fd0SJosepch Chan /* create playback/capture controls for input pins */ 3370f7278fd0SJosepch Chan static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec, 3371f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3372f7278fd0SJosepch Chan { 3373f7278fd0SJosepch Chan static char *labels[] = { 3374f7278fd0SJosepch Chan "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 3375f7278fd0SJosepch Chan }; 33760aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 3377f7278fd0SJosepch Chan int i, err, idx = 0; 3378f7278fd0SJosepch Chan 3379f7278fd0SJosepch Chan /* for internal loopback recording select */ 3380f7278fd0SJosepch Chan imux->items[imux->num_items].label = "Stereo Mixer"; 3381f7278fd0SJosepch Chan imux->items[imux->num_items].index = idx; 3382f7278fd0SJosepch Chan imux->num_items++; 3383f7278fd0SJosepch Chan 3384f7278fd0SJosepch Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 3385f7278fd0SJosepch Chan if (!cfg->input_pins[i]) 3386f7278fd0SJosepch Chan continue; 3387f7278fd0SJosepch Chan 3388f7278fd0SJosepch Chan switch (cfg->input_pins[i]) { 3389f7278fd0SJosepch Chan case 0x1a: /* Mic */ 3390f7278fd0SJosepch Chan idx = 2; 3391f7278fd0SJosepch Chan break; 3392f7278fd0SJosepch Chan 3393f7278fd0SJosepch Chan case 0x1b: /* Line In */ 3394f7278fd0SJosepch Chan idx = 3; 3395f7278fd0SJosepch Chan break; 3396f7278fd0SJosepch Chan 3397f7278fd0SJosepch Chan case 0x1e: /* Front Mic */ 3398f7278fd0SJosepch Chan idx = 4; 3399f7278fd0SJosepch Chan break; 3400f7278fd0SJosepch Chan 3401f7278fd0SJosepch Chan case 0x1f: /* CD */ 3402f7278fd0SJosepch Chan idx = 1; 3403f7278fd0SJosepch Chan break; 3404f7278fd0SJosepch Chan } 34059510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x16); 3406f7278fd0SJosepch Chan if (err < 0) 3407f7278fd0SJosepch Chan return err; 3408f7278fd0SJosepch Chan imux->items[imux->num_items].label = labels[i]; 3409f7278fd0SJosepch Chan imux->items[imux->num_items].index = idx; 3410f7278fd0SJosepch Chan imux->num_items++; 3411f7278fd0SJosepch Chan } 3412f7278fd0SJosepch Chan return 0; 3413f7278fd0SJosepch Chan } 3414f7278fd0SJosepch Chan 3415f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec) 3416f7278fd0SJosepch Chan { 3417f7278fd0SJosepch Chan struct via_spec *spec = codec->spec; 3418f7278fd0SJosepch Chan int err; 3419f7278fd0SJosepch Chan 3420f7278fd0SJosepch Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3421f7278fd0SJosepch Chan if (err < 0) 3422f7278fd0SJosepch Chan return err; 3423f7278fd0SJosepch Chan err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); 3424f7278fd0SJosepch Chan if (err < 0) 3425f7278fd0SJosepch Chan return err; 3426f7278fd0SJosepch Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3427f7278fd0SJosepch Chan return 0; /* can't find valid BIOS pin config */ 3428f7278fd0SJosepch Chan 3429f7278fd0SJosepch Chan err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); 3430f7278fd0SJosepch Chan if (err < 0) 3431f7278fd0SJosepch Chan return err; 3432f7278fd0SJosepch Chan err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3433f7278fd0SJosepch Chan if (err < 0) 3434f7278fd0SJosepch Chan return err; 3435f7278fd0SJosepch Chan err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg); 3436f7278fd0SJosepch Chan if (err < 0) 3437f7278fd0SJosepch Chan return err; 3438f7278fd0SJosepch Chan 3439f7278fd0SJosepch Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3440f7278fd0SJosepch Chan 34410852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 3442f7278fd0SJosepch Chan spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; 344355d1d6c1STakashi Iwai spec->dig_in_pin = VT1708B_DIGIN_PIN; 3444f7278fd0SJosepch Chan if (spec->autocfg.dig_in_pin) 3445f7278fd0SJosepch Chan spec->dig_in_nid = VT1708B_DIGIN_NID; 3446f7278fd0SJosepch Chan 3447603c4019STakashi Iwai if (spec->kctls.list) 3448603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3449f7278fd0SJosepch Chan 34500aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 34510aa62aefSHarald Welte 3452f8fdd495SHarald Welte if (spec->hp_mux) 34530aa62aefSHarald Welte spec->mixers[spec->num_mixers++] = via_hp_mixer; 3454f7278fd0SJosepch Chan 34551564b287SLydia Wang spec->mixers[spec->num_mixers++] = via_smart51_mixer; 3456f7278fd0SJosepch Chan return 1; 3457f7278fd0SJosepch Chan } 3458f7278fd0SJosepch Chan 3459f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3460f7278fd0SJosepch Chan static struct hda_amp_list vt1708B_loopbacks[] = { 3461f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 3462f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 3463f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 3464f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 3465f7278fd0SJosepch Chan { } /* end */ 3466f7278fd0SJosepch Chan }; 3467f7278fd0SJosepch Chan #endif 3468518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 3469f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 3470f7278fd0SJosepch Chan { 3471f7278fd0SJosepch Chan struct via_spec *spec; 3472f7278fd0SJosepch Chan int err; 3473f7278fd0SJosepch Chan 3474518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 3475518bf3baSLydia Wang return patch_vt1708S(codec); 3476f7278fd0SJosepch Chan /* create a codec specific record */ 3477eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 3478f7278fd0SJosepch Chan if (spec == NULL) 3479f7278fd0SJosepch Chan return -ENOMEM; 3480f7278fd0SJosepch Chan 3481f7278fd0SJosepch Chan codec->spec = spec; 3482f7278fd0SJosepch Chan 3483f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3484f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3485f7278fd0SJosepch Chan if (err < 0) { 3486f7278fd0SJosepch Chan via_free(codec); 3487f7278fd0SJosepch Chan return err; 3488f7278fd0SJosepch Chan } else if (!err) { 3489f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3490f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3491f7278fd0SJosepch Chan } 3492f7278fd0SJosepch Chan 349369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; 349469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3495f7278fd0SJosepch Chan 3496f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 3497f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; 3498f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3499f7278fd0SJosepch Chan 3500f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 3501f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3502f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3503f7278fd0SJosepch Chan 3504f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 3505f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 3506f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 3507337b9d02STakashi Iwai get_mux_nids(codec); 3508f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3509f7278fd0SJosepch Chan spec->num_mixers++; 3510f7278fd0SJosepch Chan } 3511f7278fd0SJosepch Chan 3512f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3513f7278fd0SJosepch Chan 3514f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 351569e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3516f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3517f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3518f7278fd0SJosepch Chan #endif 3519f7278fd0SJosepch Chan 3520f7278fd0SJosepch Chan return 0; 3521f7278fd0SJosepch Chan } 3522f7278fd0SJosepch Chan 3523f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 3524f7278fd0SJosepch Chan { 3525f7278fd0SJosepch Chan struct via_spec *spec; 3526f7278fd0SJosepch Chan int err; 3527f7278fd0SJosepch Chan 3528f7278fd0SJosepch Chan /* create a codec specific record */ 3529eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 3530f7278fd0SJosepch Chan if (spec == NULL) 3531f7278fd0SJosepch Chan return -ENOMEM; 3532f7278fd0SJosepch Chan 3533f7278fd0SJosepch Chan codec->spec = spec; 3534f7278fd0SJosepch Chan 3535f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3536f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3537f7278fd0SJosepch Chan if (err < 0) { 3538f7278fd0SJosepch Chan via_free(codec); 3539f7278fd0SJosepch Chan return err; 3540f7278fd0SJosepch Chan } else if (!err) { 3541f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3542f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3543f7278fd0SJosepch Chan } 3544f7278fd0SJosepch Chan 354569e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; 354669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3547f7278fd0SJosepch Chan 3548f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 3549f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; 3550f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3551f7278fd0SJosepch Chan 3552f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 3553f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3554f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3555f7278fd0SJosepch Chan 3556f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 3557f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 3558f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 3559337b9d02STakashi Iwai get_mux_nids(codec); 3560f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3561f7278fd0SJosepch Chan spec->num_mixers++; 3562f7278fd0SJosepch Chan } 3563f7278fd0SJosepch Chan 3564f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3565f7278fd0SJosepch Chan 3566f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 356769e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3568f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3569f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3570f7278fd0SJosepch Chan #endif 3571c577b8a1SJoseph Chan 3572c577b8a1SJoseph Chan return 0; 3573c577b8a1SJoseph Chan } 3574c577b8a1SJoseph Chan 3575d949cac1SHarald Welte /* Patch for VT1708S */ 3576d949cac1SHarald Welte 3577d949cac1SHarald Welte /* capture mixer elements */ 3578d949cac1SHarald Welte static struct snd_kcontrol_new vt1708S_capture_mixer[] = { 3579d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3580d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3581d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3582d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 35836369bcfcSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 35846369bcfcSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 35856369bcfcSLydia Wang HDA_INPUT), 3586d949cac1SHarald Welte { 3587d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3588d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3589d949cac1SHarald Welte * So call somewhat different.. 3590d949cac1SHarald Welte */ 3591d949cac1SHarald Welte /* .name = "Capture Source", */ 3592d949cac1SHarald Welte .name = "Input Source", 3593d949cac1SHarald Welte .count = 1, 3594d949cac1SHarald Welte .info = via_mux_enum_info, 3595d949cac1SHarald Welte .get = via_mux_enum_get, 3596d949cac1SHarald Welte .put = via_mux_enum_put, 3597d949cac1SHarald Welte }, 3598d949cac1SHarald Welte { } /* end */ 3599d949cac1SHarald Welte }; 3600d949cac1SHarald Welte 3601d949cac1SHarald Welte static struct hda_verb vt1708S_volume_init_verbs[] = { 3602d949cac1SHarald Welte /* Unmute ADC0-1 and set the default input to mic-in */ 3603d949cac1SHarald Welte {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3604d949cac1SHarald Welte {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3605d949cac1SHarald Welte 3606d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the 3607d949cac1SHarald Welte * analog-loopback mixer widget */ 3608d949cac1SHarald Welte /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3609d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3610d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3611d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3612d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3613d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3614d949cac1SHarald Welte 3615d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3616d949cac1SHarald Welte {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 36175691ec7fSHarald Welte /* PW9, PW10 Output enable */ 3618d949cac1SHarald Welte {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 36195691ec7fSHarald Welte {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3620d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 3621d7426329SHarald Welte {0x1, 0xf98, 0x1}, 3622bc7e7e5cSLydia Wang /* don't bybass mixer */ 3623bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 3624d949cac1SHarald Welte { } 3625d949cac1SHarald Welte }; 3626d949cac1SHarald Welte 362769e52a80SHarald Welte static struct hda_verb vt1708S_uniwill_init_verbs[] = { 3628a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3629a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3630a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3631a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3632a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3633a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3634a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3635a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3636a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 363769e52a80SHarald Welte { } 363869e52a80SHarald Welte }; 363969e52a80SHarald Welte 3640d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_playback = { 3641d949cac1SHarald Welte .substreams = 2, 3642d949cac1SHarald Welte .channels_min = 2, 3643d949cac1SHarald Welte .channels_max = 8, 3644d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 3645d949cac1SHarald Welte .ops = { 3646d949cac1SHarald Welte .open = via_playback_pcm_open, 3647c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 3648c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 364917314379SLydia Wang .close = via_pcm_open_close 3650d949cac1SHarald Welte }, 3651d949cac1SHarald Welte }; 3652d949cac1SHarald Welte 3653d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_capture = { 3654d949cac1SHarald Welte .substreams = 2, 3655d949cac1SHarald Welte .channels_min = 2, 3656d949cac1SHarald Welte .channels_max = 2, 3657d949cac1SHarald Welte .nid = 0x13, /* NID to query formats and rates */ 3658d949cac1SHarald Welte .ops = { 365917314379SLydia Wang .open = via_pcm_open_close, 3660d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 366117314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 366217314379SLydia Wang .close = via_pcm_open_close 3663d949cac1SHarald Welte }, 3664d949cac1SHarald Welte }; 3665d949cac1SHarald Welte 3666d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_digital_playback = { 36679da29271STakashi Iwai .substreams = 1, 3668d949cac1SHarald Welte .channels_min = 2, 3669d949cac1SHarald Welte .channels_max = 2, 3670d949cac1SHarald Welte /* NID is set in via_build_pcms */ 3671d949cac1SHarald Welte .ops = { 3672d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 3673d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 36749da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 36759da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3676d949cac1SHarald Welte }, 3677d949cac1SHarald Welte }; 3678d949cac1SHarald Welte 3679d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 3680d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec, 3681d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3682d949cac1SHarald Welte { 3683d949cac1SHarald Welte int i; 3684d949cac1SHarald Welte hda_nid_t nid; 3685d949cac1SHarald Welte 3686d949cac1SHarald Welte spec->multiout.num_dacs = cfg->line_outs; 3687d949cac1SHarald Welte 3688d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 3689d949cac1SHarald Welte 3690d949cac1SHarald Welte for (i = 0; i < 4; i++) { 3691d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3692d949cac1SHarald Welte if (nid) { 3693d949cac1SHarald Welte /* config dac list */ 3694d949cac1SHarald Welte switch (i) { 3695d949cac1SHarald Welte case AUTO_SEQ_FRONT: 3696d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x10; 3697d949cac1SHarald Welte break; 3698d949cac1SHarald Welte case AUTO_SEQ_CENLFE: 3699d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x24; 3700d949cac1SHarald Welte break; 3701d949cac1SHarald Welte case AUTO_SEQ_SURROUND: 3702d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x11; 3703d949cac1SHarald Welte break; 3704d949cac1SHarald Welte case AUTO_SEQ_SIDE: 3705d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x25; 3706d949cac1SHarald Welte break; 3707d949cac1SHarald Welte } 3708d949cac1SHarald Welte } 3709d949cac1SHarald Welte } 3710d949cac1SHarald Welte 3711d949cac1SHarald Welte return 0; 3712d949cac1SHarald Welte } 3713d949cac1SHarald Welte 3714d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 3715d949cac1SHarald Welte static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec, 3716d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3717d949cac1SHarald Welte { 3718d949cac1SHarald Welte char name[32]; 3719d949cac1SHarald Welte static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 3720d949cac1SHarald Welte hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25}; 3721d949cac1SHarald Welte hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27}; 3722d949cac1SHarald Welte hda_nid_t nid, nid_vol, nid_mute; 3723d949cac1SHarald Welte int i, err; 3724d949cac1SHarald Welte 3725d949cac1SHarald Welte for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3726d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3727d949cac1SHarald Welte 3728d949cac1SHarald Welte if (!nid) 3729d949cac1SHarald Welte continue; 3730d949cac1SHarald Welte 3731d949cac1SHarald Welte nid_vol = nid_vols[i]; 3732d949cac1SHarald Welte nid_mute = nid_mutes[i]; 3733d949cac1SHarald Welte 3734d949cac1SHarald Welte if (i == AUTO_SEQ_CENLFE) { 3735d949cac1SHarald Welte /* Center/LFE */ 3736d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3737d949cac1SHarald Welte "Center Playback Volume", 3738d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3739d949cac1SHarald Welte HDA_OUTPUT)); 3740d949cac1SHarald Welte if (err < 0) 3741d949cac1SHarald Welte return err; 3742d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3743d949cac1SHarald Welte "LFE Playback Volume", 3744d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3745d949cac1SHarald Welte HDA_OUTPUT)); 3746d949cac1SHarald Welte if (err < 0) 3747d949cac1SHarald Welte return err; 3748d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3749d949cac1SHarald Welte "Center Playback Switch", 3750d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3751d949cac1SHarald Welte 1, 0, 3752d949cac1SHarald Welte HDA_OUTPUT)); 3753d949cac1SHarald Welte if (err < 0) 3754d949cac1SHarald Welte return err; 3755d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3756d949cac1SHarald Welte "LFE Playback Switch", 3757d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3758d949cac1SHarald Welte 2, 0, 3759d949cac1SHarald Welte HDA_OUTPUT)); 3760d949cac1SHarald Welte if (err < 0) 3761d949cac1SHarald Welte return err; 3762d949cac1SHarald Welte } else if (i == AUTO_SEQ_FRONT) { 3763d949cac1SHarald Welte /* add control to mixer index 0 */ 3764d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3765d949cac1SHarald Welte "Master Front Playback Volume", 3766d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3767d949cac1SHarald Welte HDA_INPUT)); 3768d949cac1SHarald Welte if (err < 0) 3769d949cac1SHarald Welte return err; 3770d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3771d949cac1SHarald Welte "Master Front Playback Switch", 3772d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3773d949cac1SHarald Welte HDA_INPUT)); 3774d949cac1SHarald Welte if (err < 0) 3775d949cac1SHarald Welte return err; 3776d949cac1SHarald Welte 3777d949cac1SHarald Welte /* Front */ 3778d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3779d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3780d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3781d949cac1SHarald Welte HDA_OUTPUT)); 3782d949cac1SHarald Welte if (err < 0) 3783d949cac1SHarald Welte return err; 3784d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 3785d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3786d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3787d949cac1SHarald Welte 3, 0, 3788d949cac1SHarald Welte HDA_OUTPUT)); 3789d949cac1SHarald Welte if (err < 0) 3790d949cac1SHarald Welte return err; 3791d949cac1SHarald Welte } else { 3792d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3793d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3794d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3795d949cac1SHarald Welte HDA_OUTPUT)); 3796d949cac1SHarald Welte if (err < 0) 3797d949cac1SHarald Welte return err; 3798d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 3799d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3800d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3801d949cac1SHarald Welte 3, 0, 3802d949cac1SHarald Welte HDA_OUTPUT)); 3803d949cac1SHarald Welte if (err < 0) 3804d949cac1SHarald Welte return err; 3805d949cac1SHarald Welte } 3806d949cac1SHarald Welte } 3807d949cac1SHarald Welte 3808d949cac1SHarald Welte return 0; 3809d949cac1SHarald Welte } 3810d949cac1SHarald Welte 3811d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3812d949cac1SHarald Welte { 3813d949cac1SHarald Welte int err; 3814d949cac1SHarald Welte 3815d949cac1SHarald Welte if (!pin) 3816d949cac1SHarald Welte return 0; 3817d949cac1SHarald Welte 3818d949cac1SHarald Welte spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ 3819cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3820d949cac1SHarald Welte 3821d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3822d949cac1SHarald Welte "Headphone Playback Volume", 3823d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 3824d949cac1SHarald Welte if (err < 0) 3825d949cac1SHarald Welte return err; 3826d949cac1SHarald Welte 3827d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3828d949cac1SHarald Welte "Headphone Playback Switch", 3829d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3830d949cac1SHarald Welte if (err < 0) 3831d949cac1SHarald Welte return err; 3832d949cac1SHarald Welte 38330aa62aefSHarald Welte create_hp_imux(spec); 38340aa62aefSHarald Welte 3835d949cac1SHarald Welte return 0; 3836d949cac1SHarald Welte } 3837d949cac1SHarald Welte 3838d949cac1SHarald Welte /* create playback/capture controls for input pins */ 3839d949cac1SHarald Welte static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec, 3840d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3841d949cac1SHarald Welte { 3842d949cac1SHarald Welte static char *labels[] = { 3843d949cac1SHarald Welte "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 3844d949cac1SHarald Welte }; 38450aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 3846d949cac1SHarald Welte int i, err, idx = 0; 3847d949cac1SHarald Welte 3848d949cac1SHarald Welte /* for internal loopback recording select */ 3849d949cac1SHarald Welte imux->items[imux->num_items].label = "Stereo Mixer"; 3850d949cac1SHarald Welte imux->items[imux->num_items].index = 5; 3851d949cac1SHarald Welte imux->num_items++; 3852d949cac1SHarald Welte 3853d949cac1SHarald Welte for (i = 0; i < AUTO_PIN_LAST; i++) { 3854d949cac1SHarald Welte if (!cfg->input_pins[i]) 3855d949cac1SHarald Welte continue; 3856d949cac1SHarald Welte 3857d949cac1SHarald Welte switch (cfg->input_pins[i]) { 3858d949cac1SHarald Welte case 0x1a: /* Mic */ 3859d949cac1SHarald Welte idx = 2; 3860d949cac1SHarald Welte break; 3861d949cac1SHarald Welte 3862d949cac1SHarald Welte case 0x1b: /* Line In */ 3863d949cac1SHarald Welte idx = 3; 3864d949cac1SHarald Welte break; 3865d949cac1SHarald Welte 3866d949cac1SHarald Welte case 0x1e: /* Front Mic */ 3867d949cac1SHarald Welte idx = 4; 3868d949cac1SHarald Welte break; 3869d949cac1SHarald Welte 3870d949cac1SHarald Welte case 0x1f: /* CD */ 3871d949cac1SHarald Welte idx = 1; 3872d949cac1SHarald Welte break; 3873d949cac1SHarald Welte } 38749510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x16); 3875d949cac1SHarald Welte if (err < 0) 3876d949cac1SHarald Welte return err; 3877d949cac1SHarald Welte imux->items[imux->num_items].label = labels[i]; 3878d949cac1SHarald Welte imux->items[imux->num_items].index = idx-1; 3879d949cac1SHarald Welte imux->num_items++; 3880d949cac1SHarald Welte } 3881d949cac1SHarald Welte return 0; 3882d949cac1SHarald Welte } 3883d949cac1SHarald Welte 38849da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 38859da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 38869da29271STakashi Iwai { 38879da29271STakashi Iwai struct via_spec *spec = codec->spec; 38889da29271STakashi Iwai int i; 38899da29271STakashi Iwai 38909da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 38919da29271STakashi Iwai hda_nid_t nid; 38929da29271STakashi Iwai int conn; 38939da29271STakashi Iwai 38949da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 38959da29271STakashi Iwai if (!nid) 38969da29271STakashi Iwai continue; 38979da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 38989da29271STakashi Iwai if (conn < 1) 38999da29271STakashi Iwai continue; 39009da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 39019da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 39029da29271STakashi Iwai else { 39039da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 39049da29271STakashi Iwai break; /* at most two dig outs */ 39059da29271STakashi Iwai } 39069da29271STakashi Iwai } 39079da29271STakashi Iwai } 39089da29271STakashi Iwai 3909d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec) 3910d949cac1SHarald Welte { 3911d949cac1SHarald Welte struct via_spec *spec = codec->spec; 3912d949cac1SHarald Welte int err; 3913d949cac1SHarald Welte 39149da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3915d949cac1SHarald Welte if (err < 0) 3916d949cac1SHarald Welte return err; 3917d949cac1SHarald Welte err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); 3918d949cac1SHarald Welte if (err < 0) 3919d949cac1SHarald Welte return err; 3920d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3921d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 3922d949cac1SHarald Welte 3923d949cac1SHarald Welte err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg); 3924d949cac1SHarald Welte if (err < 0) 3925d949cac1SHarald Welte return err; 3926d949cac1SHarald Welte err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3927d949cac1SHarald Welte if (err < 0) 3928d949cac1SHarald Welte return err; 3929d949cac1SHarald Welte err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg); 3930d949cac1SHarald Welte if (err < 0) 3931d949cac1SHarald Welte return err; 3932d949cac1SHarald Welte 3933d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3934d949cac1SHarald Welte 39359da29271STakashi Iwai fill_dig_outs(codec); 393698aa34c0SHarald Welte 3937603c4019STakashi Iwai if (spec->kctls.list) 3938603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3939d949cac1SHarald Welte 39400aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 39410aa62aefSHarald Welte 3942f8fdd495SHarald Welte if (spec->hp_mux) 39430aa62aefSHarald Welte spec->mixers[spec->num_mixers++] = via_hp_mixer; 3944d949cac1SHarald Welte 39451564b287SLydia Wang spec->mixers[spec->num_mixers++] = via_smart51_mixer; 3946d949cac1SHarald Welte return 1; 3947d949cac1SHarald Welte } 3948d949cac1SHarald Welte 3949d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3950d949cac1SHarald Welte static struct hda_amp_list vt1708S_loopbacks[] = { 3951d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 3952d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 3953d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 3954d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 3955d949cac1SHarald Welte { } /* end */ 3956d949cac1SHarald Welte }; 3957d949cac1SHarald Welte #endif 3958d949cac1SHarald Welte 39596369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 39606369bcfcSLydia Wang int offset, int num_steps, int step_size) 39616369bcfcSLydia Wang { 39626369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 39636369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 39646369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 39656369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 39666369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 39676369bcfcSLydia Wang } 39686369bcfcSLydia Wang 3969d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 3970d949cac1SHarald Welte { 3971d949cac1SHarald Welte struct via_spec *spec; 3972d949cac1SHarald Welte int err; 3973d949cac1SHarald Welte 3974d949cac1SHarald Welte /* create a codec specific record */ 3975d949cac1SHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 3976d949cac1SHarald Welte if (spec == NULL) 3977d949cac1SHarald Welte return -ENOMEM; 3978d949cac1SHarald Welte 3979d949cac1SHarald Welte codec->spec = spec; 3980d949cac1SHarald Welte 3981d949cac1SHarald Welte /* automatic parse from the BIOS config */ 3982d949cac1SHarald Welte err = vt1708S_parse_auto_config(codec); 3983d949cac1SHarald Welte if (err < 0) { 3984d949cac1SHarald Welte via_free(codec); 3985d949cac1SHarald Welte return err; 3986d949cac1SHarald Welte } else if (!err) { 3987d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 3988d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 3989d949cac1SHarald Welte } 3990d949cac1SHarald Welte 399169e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; 399269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs; 3993d949cac1SHarald Welte 3994d949cac1SHarald Welte spec->stream_name_analog = "VT1708S Analog"; 3995d949cac1SHarald Welte spec->stream_analog_playback = &vt1708S_pcm_analog_playback; 3996d949cac1SHarald Welte spec->stream_analog_capture = &vt1708S_pcm_analog_capture; 3997d949cac1SHarald Welte 3998d949cac1SHarald Welte spec->stream_name_digital = "VT1708S Digital"; 3999d949cac1SHarald Welte spec->stream_digital_playback = &vt1708S_pcm_digital_playback; 4000d949cac1SHarald Welte 4001d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 4002d949cac1SHarald Welte spec->adc_nids = vt1708S_adc_nids; 4003d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); 4004337b9d02STakashi Iwai get_mux_nids(codec); 40056369bcfcSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 40066369bcfcSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 4007d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; 4008d949cac1SHarald Welte spec->num_mixers++; 4009d949cac1SHarald Welte } 4010d949cac1SHarald Welte 4011d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 4012d949cac1SHarald Welte 4013d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 401469e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 4015d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4016d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 4017d949cac1SHarald Welte #endif 4018d949cac1SHarald Welte 4019518bf3baSLydia Wang /* correct names for VT1708BCE */ 4020518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 4021518bf3baSLydia Wang kfree(codec->chip_name); 4022518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 4023518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 4024518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 4025518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 4026518bf3baSLydia Wang spec->stream_name_analog = "VT1708BCE Analog"; 4027518bf3baSLydia Wang spec->stream_name_digital = "VT1708BCE Digital"; 4028518bf3baSLydia Wang } 4029d949cac1SHarald Welte return 0; 4030d949cac1SHarald Welte } 4031d949cac1SHarald Welte 4032d949cac1SHarald Welte /* Patch for VT1702 */ 4033d949cac1SHarald Welte 4034d949cac1SHarald Welte /* capture mixer elements */ 4035d949cac1SHarald Welte static struct snd_kcontrol_new vt1702_capture_mixer[] = { 4036d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), 4037d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), 4038d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), 4039d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), 4040d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), 4041d949cac1SHarald Welte HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), 4042d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, 4043d949cac1SHarald Welte HDA_INPUT), 4044d949cac1SHarald Welte { 4045d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4046d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 4047d949cac1SHarald Welte * So call somewhat different.. 4048d949cac1SHarald Welte */ 4049d949cac1SHarald Welte /* .name = "Capture Source", */ 4050d949cac1SHarald Welte .name = "Input Source", 4051d949cac1SHarald Welte .count = 1, 4052d949cac1SHarald Welte .info = via_mux_enum_info, 4053d949cac1SHarald Welte .get = via_mux_enum_get, 4054d949cac1SHarald Welte .put = via_mux_enum_put, 4055d949cac1SHarald Welte }, 4056d949cac1SHarald Welte { } /* end */ 4057d949cac1SHarald Welte }; 4058d949cac1SHarald Welte 4059d949cac1SHarald Welte static struct hda_verb vt1702_volume_init_verbs[] = { 4060d949cac1SHarald Welte /* 4061d949cac1SHarald Welte * Unmute ADC0-1 and set the default input to mic-in 4062d949cac1SHarald Welte */ 4063d949cac1SHarald Welte {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4064d949cac1SHarald Welte {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4065d949cac1SHarald Welte {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4066d949cac1SHarald Welte 4067d949cac1SHarald Welte 4068d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4069d949cac1SHarald Welte * mixer widget 4070d949cac1SHarald Welte */ 4071d949cac1SHarald Welte /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ 4072d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4073d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4074d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 4075d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 4076d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4077d949cac1SHarald Welte 4078d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 4079d949cac1SHarald Welte {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, 4080d949cac1SHarald Welte /* PW6 PW7 Output enable */ 4081d949cac1SHarald Welte {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4082d949cac1SHarald Welte {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4083bc7e7e5cSLydia Wang /* mixer enable */ 4084bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 4085bc7e7e5cSLydia Wang /* GPIO 0~2 */ 4086bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 4087d949cac1SHarald Welte { } 4088d949cac1SHarald Welte }; 4089d949cac1SHarald Welte 409069e52a80SHarald Welte static struct hda_verb vt1702_uniwill_init_verbs[] = { 4091a34df19aSLydia Wang {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, 4092a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4093a34df19aSLydia Wang {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4094a34df19aSLydia Wang {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4095a34df19aSLydia Wang {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4096a34df19aSLydia Wang {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 409769e52a80SHarald Welte { } 409869e52a80SHarald Welte }; 409969e52a80SHarald Welte 4100d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_playback = { 41010aa62aefSHarald Welte .substreams = 2, 4102d949cac1SHarald Welte .channels_min = 2, 4103d949cac1SHarald Welte .channels_max = 2, 4104d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 4105d949cac1SHarald Welte .ops = { 4106d949cac1SHarald Welte .open = via_playback_pcm_open, 41070aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 410817314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 410917314379SLydia Wang .close = via_pcm_open_close 4110d949cac1SHarald Welte }, 4111d949cac1SHarald Welte }; 4112d949cac1SHarald Welte 4113d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_capture = { 4114d949cac1SHarald Welte .substreams = 3, 4115d949cac1SHarald Welte .channels_min = 2, 4116d949cac1SHarald Welte .channels_max = 2, 4117d949cac1SHarald Welte .nid = 0x12, /* NID to query formats and rates */ 4118d949cac1SHarald Welte .ops = { 411917314379SLydia Wang .open = via_pcm_open_close, 4120d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 412117314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 412217314379SLydia Wang .close = via_pcm_open_close 4123d949cac1SHarald Welte }, 4124d949cac1SHarald Welte }; 4125d949cac1SHarald Welte 4126d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_digital_playback = { 41275691ec7fSHarald Welte .substreams = 2, 4128d949cac1SHarald Welte .channels_min = 2, 4129d949cac1SHarald Welte .channels_max = 2, 4130d949cac1SHarald Welte /* NID is set in via_build_pcms */ 4131d949cac1SHarald Welte .ops = { 4132d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 4133d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 41349da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 41359da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 4136d949cac1SHarald Welte }, 4137d949cac1SHarald Welte }; 4138d949cac1SHarald Welte 4139d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 4140d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec, 4141d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4142d949cac1SHarald Welte { 4143d949cac1SHarald Welte spec->multiout.num_dacs = 1; 4144d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 4145d949cac1SHarald Welte 4146d949cac1SHarald Welte if (cfg->line_out_pins[0]) { 4147d949cac1SHarald Welte /* config dac list */ 4148d949cac1SHarald Welte spec->multiout.dac_nids[0] = 0x10; 4149d949cac1SHarald Welte } 4150d949cac1SHarald Welte 4151d949cac1SHarald Welte return 0; 4152d949cac1SHarald Welte } 4153d949cac1SHarald Welte 4154d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 4155d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, 4156d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4157d949cac1SHarald Welte { 4158d949cac1SHarald Welte int err; 4159d949cac1SHarald Welte 4160d949cac1SHarald Welte if (!cfg->line_out_pins[0]) 4161d949cac1SHarald Welte return -1; 4162d949cac1SHarald Welte 4163d949cac1SHarald Welte /* add control to mixer index 0 */ 4164d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4165d949cac1SHarald Welte "Master Front Playback Volume", 4166d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4167d949cac1SHarald Welte if (err < 0) 4168d949cac1SHarald Welte return err; 4169d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4170d949cac1SHarald Welte "Master Front Playback Switch", 4171d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4172d949cac1SHarald Welte if (err < 0) 4173d949cac1SHarald Welte return err; 4174d949cac1SHarald Welte 4175d949cac1SHarald Welte /* Front */ 4176d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4177d949cac1SHarald Welte "Front Playback Volume", 4178d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT)); 4179d949cac1SHarald Welte if (err < 0) 4180d949cac1SHarald Welte return err; 4181d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4182d949cac1SHarald Welte "Front Playback Switch", 4183d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT)); 4184d949cac1SHarald Welte if (err < 0) 4185d949cac1SHarald Welte return err; 4186d949cac1SHarald Welte 4187d949cac1SHarald Welte return 0; 4188d949cac1SHarald Welte } 4189d949cac1SHarald Welte 4190d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4191d949cac1SHarald Welte { 41920713efebSLydia Wang int err, i; 41930713efebSLydia Wang struct hda_input_mux *imux; 41940713efebSLydia Wang static const char *texts[] = { "ON", "OFF", NULL}; 4195d949cac1SHarald Welte if (!pin) 4196d949cac1SHarald Welte return 0; 4197d949cac1SHarald Welte spec->multiout.hp_nid = 0x1D; 4198cdc1784dSLydia Wang spec->hp_independent_mode_index = 0; 4199d949cac1SHarald Welte 4200d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4201d949cac1SHarald Welte "Headphone Playback Volume", 4202d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT)); 4203d949cac1SHarald Welte if (err < 0) 4204d949cac1SHarald Welte return err; 4205d949cac1SHarald Welte 4206d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4207d949cac1SHarald Welte "Headphone Playback Switch", 4208d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4209d949cac1SHarald Welte if (err < 0) 4210d949cac1SHarald Welte return err; 4211d949cac1SHarald Welte 42120713efebSLydia Wang imux = &spec->private_imux[1]; 42130aa62aefSHarald Welte 42140713efebSLydia Wang /* for hp mode select */ 42150713efebSLydia Wang i = 0; 42160713efebSLydia Wang while (texts[i] != NULL) { 42170713efebSLydia Wang imux->items[imux->num_items].label = texts[i]; 42180713efebSLydia Wang imux->items[imux->num_items].index = i; 42190713efebSLydia Wang imux->num_items++; 42200713efebSLydia Wang i++; 42210713efebSLydia Wang } 42220713efebSLydia Wang 42230713efebSLydia Wang spec->hp_mux = &spec->private_imux[1]; 4224d949cac1SHarald Welte return 0; 4225d949cac1SHarald Welte } 4226d949cac1SHarald Welte 4227d949cac1SHarald Welte /* create playback/capture controls for input pins */ 4228d949cac1SHarald Welte static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec, 4229d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4230d949cac1SHarald Welte { 4231d949cac1SHarald Welte static char *labels[] = { 4232d949cac1SHarald Welte "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 4233d949cac1SHarald Welte }; 42340aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 4235d949cac1SHarald Welte int i, err, idx = 0; 4236d949cac1SHarald Welte 4237d949cac1SHarald Welte /* for internal loopback recording select */ 4238d949cac1SHarald Welte imux->items[imux->num_items].label = "Stereo Mixer"; 4239d949cac1SHarald Welte imux->items[imux->num_items].index = 3; 4240d949cac1SHarald Welte imux->num_items++; 4241d949cac1SHarald Welte 4242d949cac1SHarald Welte for (i = 0; i < AUTO_PIN_LAST; i++) { 4243d949cac1SHarald Welte if (!cfg->input_pins[i]) 4244d949cac1SHarald Welte continue; 4245d949cac1SHarald Welte 4246d949cac1SHarald Welte switch (cfg->input_pins[i]) { 4247d949cac1SHarald Welte case 0x14: /* Mic */ 4248d949cac1SHarald Welte idx = 1; 4249d949cac1SHarald Welte break; 4250d949cac1SHarald Welte 4251d949cac1SHarald Welte case 0x15: /* Line In */ 4252d949cac1SHarald Welte idx = 2; 4253d949cac1SHarald Welte break; 4254d949cac1SHarald Welte 4255d949cac1SHarald Welte case 0x18: /* Front Mic */ 4256d949cac1SHarald Welte idx = 3; 4257d949cac1SHarald Welte break; 4258d949cac1SHarald Welte } 42599510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x1A); 4260d949cac1SHarald Welte if (err < 0) 4261d949cac1SHarald Welte return err; 4262d949cac1SHarald Welte imux->items[imux->num_items].label = labels[i]; 4263d949cac1SHarald Welte imux->items[imux->num_items].index = idx-1; 4264d949cac1SHarald Welte imux->num_items++; 4265d949cac1SHarald Welte } 4266d949cac1SHarald Welte return 0; 4267d949cac1SHarald Welte } 4268d949cac1SHarald Welte 4269d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec) 4270d949cac1SHarald Welte { 4271d949cac1SHarald Welte struct via_spec *spec = codec->spec; 4272d949cac1SHarald Welte int err; 4273d949cac1SHarald Welte 42749da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4275d949cac1SHarald Welte if (err < 0) 4276d949cac1SHarald Welte return err; 4277d949cac1SHarald Welte err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); 4278d949cac1SHarald Welte if (err < 0) 4279d949cac1SHarald Welte return err; 4280d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4281d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 4282d949cac1SHarald Welte 4283d949cac1SHarald Welte err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg); 4284d949cac1SHarald Welte if (err < 0) 4285d949cac1SHarald Welte return err; 4286d949cac1SHarald Welte err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4287d949cac1SHarald Welte if (err < 0) 4288d949cac1SHarald Welte return err; 4289c2c02ea3SLydia Wang /* limit AA path volume to 0 dB */ 4290c2c02ea3SLydia Wang snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 4291c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 4292c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 4293c2c02ea3SLydia Wang (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 4294c2c02ea3SLydia Wang (1 << AC_AMPCAP_MUTE_SHIFT)); 4295d949cac1SHarald Welte err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg); 4296d949cac1SHarald Welte if (err < 0) 4297d949cac1SHarald Welte return err; 4298d949cac1SHarald Welte 4299d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4300d949cac1SHarald Welte 43019da29271STakashi Iwai fill_dig_outs(codec); 430298aa34c0SHarald Welte 4303603c4019STakashi Iwai if (spec->kctls.list) 4304603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 4305d949cac1SHarald Welte 43060aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 43070aa62aefSHarald Welte 4308f8fdd495SHarald Welte if (spec->hp_mux) 43090aa62aefSHarald Welte spec->mixers[spec->num_mixers++] = via_hp_mixer; 4310d949cac1SHarald Welte 4311d949cac1SHarald Welte return 1; 4312d949cac1SHarald Welte } 4313d949cac1SHarald Welte 4314d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4315d949cac1SHarald Welte static struct hda_amp_list vt1702_loopbacks[] = { 4316d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 4317d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 4318d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 4319d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 4320d949cac1SHarald Welte { } /* end */ 4321d949cac1SHarald Welte }; 4322d949cac1SHarald Welte #endif 4323d949cac1SHarald Welte 4324d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 4325d949cac1SHarald Welte { 4326d949cac1SHarald Welte struct via_spec *spec; 4327d949cac1SHarald Welte int err; 4328d949cac1SHarald Welte 4329d949cac1SHarald Welte /* create a codec specific record */ 4330d949cac1SHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 4331d949cac1SHarald Welte if (spec == NULL) 4332d949cac1SHarald Welte return -ENOMEM; 4333d949cac1SHarald Welte 4334d949cac1SHarald Welte codec->spec = spec; 4335d949cac1SHarald Welte 4336d949cac1SHarald Welte /* automatic parse from the BIOS config */ 4337d949cac1SHarald Welte err = vt1702_parse_auto_config(codec); 4338d949cac1SHarald Welte if (err < 0) { 4339d949cac1SHarald Welte via_free(codec); 4340d949cac1SHarald Welte return err; 4341d949cac1SHarald Welte } else if (!err) { 4342d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 4343d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 4344d949cac1SHarald Welte } 4345d949cac1SHarald Welte 434669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; 434769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; 4348d949cac1SHarald Welte 4349d949cac1SHarald Welte spec->stream_name_analog = "VT1702 Analog"; 4350d949cac1SHarald Welte spec->stream_analog_playback = &vt1702_pcm_analog_playback; 4351d949cac1SHarald Welte spec->stream_analog_capture = &vt1702_pcm_analog_capture; 4352d949cac1SHarald Welte 4353d949cac1SHarald Welte spec->stream_name_digital = "VT1702 Digital"; 4354d949cac1SHarald Welte spec->stream_digital_playback = &vt1702_pcm_digital_playback; 4355d949cac1SHarald Welte 4356d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 4357d949cac1SHarald Welte spec->adc_nids = vt1702_adc_nids; 4358d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids); 4359337b9d02STakashi Iwai get_mux_nids(codec); 4360d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1702_capture_mixer; 4361d949cac1SHarald Welte spec->num_mixers++; 4362d949cac1SHarald Welte } 4363d949cac1SHarald Welte 4364d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 4365d949cac1SHarald Welte 4366d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 436769e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 4368d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4369d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 4370d949cac1SHarald Welte #endif 4371d949cac1SHarald Welte 4372d949cac1SHarald Welte return 0; 4373d949cac1SHarald Welte } 4374d949cac1SHarald Welte 4375eb7188caSLydia Wang /* Patch for VT1718S */ 4376eb7188caSLydia Wang 4377eb7188caSLydia Wang /* capture mixer elements */ 4378eb7188caSLydia Wang static struct snd_kcontrol_new vt1718S_capture_mixer[] = { 4379eb7188caSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 4380eb7188caSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 4381eb7188caSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 4382eb7188caSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 4383eb7188caSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 4384eb7188caSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 4385eb7188caSLydia Wang HDA_INPUT), 4386eb7188caSLydia Wang { 4387eb7188caSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4388eb7188caSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 4389eb7188caSLydia Wang * So call somewhat different.. 4390eb7188caSLydia Wang */ 4391eb7188caSLydia Wang .name = "Input Source", 4392eb7188caSLydia Wang .count = 2, 4393eb7188caSLydia Wang .info = via_mux_enum_info, 4394eb7188caSLydia Wang .get = via_mux_enum_get, 4395eb7188caSLydia Wang .put = via_mux_enum_put, 4396eb7188caSLydia Wang }, 4397eb7188caSLydia Wang { } /* end */ 4398eb7188caSLydia Wang }; 4399eb7188caSLydia Wang 4400eb7188caSLydia Wang static struct hda_verb vt1718S_volume_init_verbs[] = { 4401eb7188caSLydia Wang /* 4402eb7188caSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4403eb7188caSLydia Wang */ 4404eb7188caSLydia Wang {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4405eb7188caSLydia Wang {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4406eb7188caSLydia Wang 4407eb7188caSLydia Wang 4408eb7188caSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4409eb7188caSLydia Wang * mixer widget 4410eb7188caSLydia Wang */ 4411eb7188caSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4412eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4413eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4414eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4415eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4416eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, 4417eb7188caSLydia Wang 4418eb7188caSLydia Wang /* Setup default input of Front HP to MW9 */ 4419eb7188caSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 4420eb7188caSLydia Wang /* PW9 PW10 Output enable */ 4421eb7188caSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4422eb7188caSLydia Wang {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4423eb7188caSLydia Wang /* PW11 Input enable */ 4424eb7188caSLydia Wang {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, 4425eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 4426eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 4427eb7188caSLydia Wang /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ 4428eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4429eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4430eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4431eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4432eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4433eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4434eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4435eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4436eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4437eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4438eb7188caSLydia Wang /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ 4439eb7188caSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, 4440eb7188caSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, 4441eb7188caSLydia Wang /* Unmute MW4's index 0 */ 4442eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4443eb7188caSLydia Wang { } 4444eb7188caSLydia Wang }; 4445eb7188caSLydia Wang 4446eb7188caSLydia Wang 4447eb7188caSLydia Wang static struct hda_verb vt1718S_uniwill_init_verbs[] = { 4448eb7188caSLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 4449eb7188caSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4450eb7188caSLydia Wang {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4451eb7188caSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4452eb7188caSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4453eb7188caSLydia Wang {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4454eb7188caSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4455eb7188caSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4456eb7188caSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4457eb7188caSLydia Wang { } 4458eb7188caSLydia Wang }; 4459eb7188caSLydia Wang 4460eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_playback = { 4461eb7188caSLydia Wang .substreams = 2, 4462eb7188caSLydia Wang .channels_min = 2, 4463eb7188caSLydia Wang .channels_max = 10, 4464eb7188caSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 4465eb7188caSLydia Wang .ops = { 4466eb7188caSLydia Wang .open = via_playback_pcm_open, 4467eb7188caSLydia Wang .prepare = via_playback_multi_pcm_prepare, 4468eb7188caSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 4469eb7188caSLydia Wang .close = via_pcm_open_close, 4470eb7188caSLydia Wang }, 4471eb7188caSLydia Wang }; 4472eb7188caSLydia Wang 4473eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_capture = { 4474eb7188caSLydia Wang .substreams = 2, 4475eb7188caSLydia Wang .channels_min = 2, 4476eb7188caSLydia Wang .channels_max = 2, 4477eb7188caSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 4478eb7188caSLydia Wang .ops = { 4479eb7188caSLydia Wang .open = via_pcm_open_close, 4480eb7188caSLydia Wang .prepare = via_capture_pcm_prepare, 4481eb7188caSLydia Wang .cleanup = via_capture_pcm_cleanup, 4482eb7188caSLydia Wang .close = via_pcm_open_close, 4483eb7188caSLydia Wang }, 4484eb7188caSLydia Wang }; 4485eb7188caSLydia Wang 4486eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_playback = { 4487eb7188caSLydia Wang .substreams = 2, 4488eb7188caSLydia Wang .channels_min = 2, 4489eb7188caSLydia Wang .channels_max = 2, 4490eb7188caSLydia Wang .rates = SNDRV_PCM_RATE_48000, 4491eb7188caSLydia Wang /* NID is set in via_build_pcms */ 4492eb7188caSLydia Wang .ops = { 4493eb7188caSLydia Wang .open = via_dig_playback_pcm_open, 4494eb7188caSLydia Wang .close = via_dig_playback_pcm_close, 4495eb7188caSLydia Wang .prepare = via_dig_playback_pcm_prepare, 4496eb7188caSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 4497eb7188caSLydia Wang }, 4498eb7188caSLydia Wang }; 4499eb7188caSLydia Wang 4500eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_capture = { 4501eb7188caSLydia Wang .substreams = 1, 4502eb7188caSLydia Wang .channels_min = 2, 4503eb7188caSLydia Wang .channels_max = 2, 4504eb7188caSLydia Wang }; 4505eb7188caSLydia Wang 4506eb7188caSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 4507eb7188caSLydia Wang static int vt1718S_auto_fill_dac_nids(struct via_spec *spec, 4508eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4509eb7188caSLydia Wang { 4510eb7188caSLydia Wang int i; 4511eb7188caSLydia Wang hda_nid_t nid; 4512eb7188caSLydia Wang 4513eb7188caSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 4514eb7188caSLydia Wang 4515eb7188caSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 4516eb7188caSLydia Wang 4517eb7188caSLydia Wang for (i = 0; i < 4; i++) { 4518eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4519eb7188caSLydia Wang if (nid) { 4520eb7188caSLydia Wang /* config dac list */ 4521eb7188caSLydia Wang switch (i) { 4522eb7188caSLydia Wang case AUTO_SEQ_FRONT: 4523eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0x8; 4524eb7188caSLydia Wang break; 4525eb7188caSLydia Wang case AUTO_SEQ_CENLFE: 4526eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0xa; 4527eb7188caSLydia Wang break; 4528eb7188caSLydia Wang case AUTO_SEQ_SURROUND: 4529eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0x9; 4530eb7188caSLydia Wang break; 4531eb7188caSLydia Wang case AUTO_SEQ_SIDE: 4532eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0xb; 4533eb7188caSLydia Wang break; 4534eb7188caSLydia Wang } 4535eb7188caSLydia Wang } 4536eb7188caSLydia Wang } 4537eb7188caSLydia Wang 4538eb7188caSLydia Wang return 0; 4539eb7188caSLydia Wang } 4540eb7188caSLydia Wang 4541eb7188caSLydia Wang /* add playback controls from the parsed DAC table */ 4542eb7188caSLydia Wang static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec, 4543eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4544eb7188caSLydia Wang { 4545eb7188caSLydia Wang char name[32]; 4546eb7188caSLydia Wang static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 4547eb7188caSLydia Wang hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb}; 4548eb7188caSLydia Wang hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27}; 4549eb7188caSLydia Wang hda_nid_t nid, nid_vol, nid_mute = 0; 4550eb7188caSLydia Wang int i, err; 4551eb7188caSLydia Wang 4552eb7188caSLydia Wang for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 4553eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4554eb7188caSLydia Wang 4555eb7188caSLydia Wang if (!nid) 4556eb7188caSLydia Wang continue; 4557eb7188caSLydia Wang nid_vol = nid_vols[i]; 4558eb7188caSLydia Wang nid_mute = nid_mutes[i]; 4559eb7188caSLydia Wang 4560eb7188caSLydia Wang if (i == AUTO_SEQ_CENLFE) { 4561eb7188caSLydia Wang /* Center/LFE */ 4562eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4563eb7188caSLydia Wang "Center Playback Volume", 4564eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 4565eb7188caSLydia Wang HDA_OUTPUT)); 4566eb7188caSLydia Wang if (err < 0) 4567eb7188caSLydia Wang return err; 4568eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4569eb7188caSLydia Wang "LFE Playback Volume", 4570eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 4571eb7188caSLydia Wang HDA_OUTPUT)); 4572eb7188caSLydia Wang if (err < 0) 4573eb7188caSLydia Wang return err; 4574eb7188caSLydia Wang err = via_add_control( 4575eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4576eb7188caSLydia Wang "Center Playback Switch", 4577eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 4578eb7188caSLydia Wang HDA_OUTPUT)); 4579eb7188caSLydia Wang if (err < 0) 4580eb7188caSLydia Wang return err; 4581eb7188caSLydia Wang err = via_add_control( 4582eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4583eb7188caSLydia Wang "LFE Playback Switch", 4584eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 4585eb7188caSLydia Wang HDA_OUTPUT)); 4586eb7188caSLydia Wang if (err < 0) 4587eb7188caSLydia Wang return err; 4588eb7188caSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 4589eb7188caSLydia Wang /* Front */ 4590eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4591eb7188caSLydia Wang err = via_add_control( 4592eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4593eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4594eb7188caSLydia Wang if (err < 0) 4595eb7188caSLydia Wang return err; 4596eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4597eb7188caSLydia Wang err = via_add_control( 4598eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4599eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4600eb7188caSLydia Wang HDA_OUTPUT)); 4601eb7188caSLydia Wang if (err < 0) 4602eb7188caSLydia Wang return err; 4603eb7188caSLydia Wang } else { 4604eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4605eb7188caSLydia Wang err = via_add_control( 4606eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4607eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4608eb7188caSLydia Wang if (err < 0) 4609eb7188caSLydia Wang return err; 4610eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4611eb7188caSLydia Wang err = via_add_control( 4612eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4613eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4614eb7188caSLydia Wang HDA_OUTPUT)); 4615eb7188caSLydia Wang if (err < 0) 4616eb7188caSLydia Wang return err; 4617eb7188caSLydia Wang } 4618eb7188caSLydia Wang } 4619eb7188caSLydia Wang return 0; 4620eb7188caSLydia Wang } 4621eb7188caSLydia Wang 4622eb7188caSLydia Wang static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4623eb7188caSLydia Wang { 4624eb7188caSLydia Wang int err; 4625eb7188caSLydia Wang 4626eb7188caSLydia Wang if (!pin) 4627eb7188caSLydia Wang return 0; 4628eb7188caSLydia Wang 4629eb7188caSLydia Wang spec->multiout.hp_nid = 0xc; /* AOW4 */ 4630eb7188caSLydia Wang spec->hp_independent_mode_index = 1; 4631eb7188caSLydia Wang 4632eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4633eb7188caSLydia Wang "Headphone Playback Volume", 4634eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT)); 4635eb7188caSLydia Wang if (err < 0) 4636eb7188caSLydia Wang return err; 4637eb7188caSLydia Wang 4638eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4639eb7188caSLydia Wang "Headphone Playback Switch", 4640eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4641eb7188caSLydia Wang if (err < 0) 4642eb7188caSLydia Wang return err; 4643eb7188caSLydia Wang 4644eb7188caSLydia Wang create_hp_imux(spec); 4645eb7188caSLydia Wang return 0; 4646eb7188caSLydia Wang } 4647eb7188caSLydia Wang 4648eb7188caSLydia Wang /* create playback/capture controls for input pins */ 4649eb7188caSLydia Wang static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec, 4650eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4651eb7188caSLydia Wang { 4652eb7188caSLydia Wang static char *labels[] = { 4653eb7188caSLydia Wang "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 4654eb7188caSLydia Wang }; 4655eb7188caSLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 4656eb7188caSLydia Wang int i, err, idx = 0; 4657eb7188caSLydia Wang 4658eb7188caSLydia Wang /* for internal loopback recording select */ 4659eb7188caSLydia Wang imux->items[imux->num_items].label = "Stereo Mixer"; 4660eb7188caSLydia Wang imux->items[imux->num_items].index = 5; 4661eb7188caSLydia Wang imux->num_items++; 4662eb7188caSLydia Wang 4663eb7188caSLydia Wang for (i = 0; i < AUTO_PIN_LAST; i++) { 4664eb7188caSLydia Wang if (!cfg->input_pins[i]) 4665eb7188caSLydia Wang continue; 4666eb7188caSLydia Wang 4667eb7188caSLydia Wang switch (cfg->input_pins[i]) { 4668eb7188caSLydia Wang case 0x2b: /* Mic */ 4669eb7188caSLydia Wang idx = 1; 4670eb7188caSLydia Wang break; 4671eb7188caSLydia Wang 4672eb7188caSLydia Wang case 0x2a: /* Line In */ 4673eb7188caSLydia Wang idx = 2; 4674eb7188caSLydia Wang break; 4675eb7188caSLydia Wang 4676eb7188caSLydia Wang case 0x29: /* Front Mic */ 4677eb7188caSLydia Wang idx = 3; 4678eb7188caSLydia Wang break; 4679eb7188caSLydia Wang 4680eb7188caSLydia Wang case 0x2c: /* CD */ 4681eb7188caSLydia Wang idx = 0; 4682eb7188caSLydia Wang break; 4683eb7188caSLydia Wang } 4684eb7188caSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x21); 4685eb7188caSLydia Wang if (err < 0) 4686eb7188caSLydia Wang return err; 4687eb7188caSLydia Wang imux->items[imux->num_items].label = labels[i]; 4688eb7188caSLydia Wang imux->items[imux->num_items].index = idx; 4689eb7188caSLydia Wang imux->num_items++; 4690eb7188caSLydia Wang } 4691eb7188caSLydia Wang return 0; 4692eb7188caSLydia Wang } 4693eb7188caSLydia Wang 4694eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec) 4695eb7188caSLydia Wang { 4696eb7188caSLydia Wang struct via_spec *spec = codec->spec; 4697eb7188caSLydia Wang int err; 4698eb7188caSLydia Wang 4699eb7188caSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4700eb7188caSLydia Wang 4701eb7188caSLydia Wang if (err < 0) 4702eb7188caSLydia Wang return err; 4703eb7188caSLydia Wang err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg); 4704eb7188caSLydia Wang if (err < 0) 4705eb7188caSLydia Wang return err; 4706eb7188caSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4707eb7188caSLydia Wang return 0; /* can't find valid BIOS pin config */ 4708eb7188caSLydia Wang 4709eb7188caSLydia Wang err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg); 4710eb7188caSLydia Wang if (err < 0) 4711eb7188caSLydia Wang return err; 4712eb7188caSLydia Wang err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4713eb7188caSLydia Wang if (err < 0) 4714eb7188caSLydia Wang return err; 4715eb7188caSLydia Wang err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg); 4716eb7188caSLydia Wang if (err < 0) 4717eb7188caSLydia Wang return err; 4718eb7188caSLydia Wang 4719eb7188caSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4720eb7188caSLydia Wang 4721eb7188caSLydia Wang fill_dig_outs(codec); 4722eb7188caSLydia Wang 4723eb7188caSLydia Wang if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) 4724eb7188caSLydia Wang spec->dig_in_nid = 0x13; 4725eb7188caSLydia Wang 4726eb7188caSLydia Wang if (spec->kctls.list) 4727eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 4728eb7188caSLydia Wang 4729eb7188caSLydia Wang spec->input_mux = &spec->private_imux[0]; 4730eb7188caSLydia Wang 4731eb7188caSLydia Wang if (spec->hp_mux) 4732eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = via_hp_mixer; 4733eb7188caSLydia Wang 4734eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = via_smart51_mixer; 4735eb7188caSLydia Wang 4736eb7188caSLydia Wang return 1; 4737eb7188caSLydia Wang } 4738eb7188caSLydia Wang 4739eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4740eb7188caSLydia Wang static struct hda_amp_list vt1718S_loopbacks[] = { 4741eb7188caSLydia Wang { 0x21, HDA_INPUT, 1 }, 4742eb7188caSLydia Wang { 0x21, HDA_INPUT, 2 }, 4743eb7188caSLydia Wang { 0x21, HDA_INPUT, 3 }, 4744eb7188caSLydia Wang { 0x21, HDA_INPUT, 4 }, 4745eb7188caSLydia Wang { } /* end */ 4746eb7188caSLydia Wang }; 4747eb7188caSLydia Wang #endif 4748eb7188caSLydia Wang 4749eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 4750eb7188caSLydia Wang { 4751eb7188caSLydia Wang struct via_spec *spec; 4752eb7188caSLydia Wang int err; 4753eb7188caSLydia Wang 4754eb7188caSLydia Wang /* create a codec specific record */ 4755eb7188caSLydia Wang spec = kzalloc(sizeof(*spec), GFP_KERNEL); 4756eb7188caSLydia Wang if (spec == NULL) 4757eb7188caSLydia Wang return -ENOMEM; 4758eb7188caSLydia Wang 4759eb7188caSLydia Wang codec->spec = spec; 4760eb7188caSLydia Wang 4761eb7188caSLydia Wang /* automatic parse from the BIOS config */ 4762eb7188caSLydia Wang err = vt1718S_parse_auto_config(codec); 4763eb7188caSLydia Wang if (err < 0) { 4764eb7188caSLydia Wang via_free(codec); 4765eb7188caSLydia Wang return err; 4766eb7188caSLydia Wang } else if (!err) { 4767eb7188caSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 4768eb7188caSLydia Wang "from BIOS. Using genenic mode...\n"); 4769eb7188caSLydia Wang } 4770eb7188caSLydia Wang 4771eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; 4772eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; 4773eb7188caSLydia Wang 4774bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060441) 4775bb3c6bfcSLydia Wang spec->stream_name_analog = "VT2020 Analog"; 4776bb3c6bfcSLydia Wang else if (codec->vendor_id == 0x11064441) 4777bb3c6bfcSLydia Wang spec->stream_name_analog = "VT1828S Analog"; 4778bb3c6bfcSLydia Wang else 4779eb7188caSLydia Wang spec->stream_name_analog = "VT1718S Analog"; 4780eb7188caSLydia Wang spec->stream_analog_playback = &vt1718S_pcm_analog_playback; 4781eb7188caSLydia Wang spec->stream_analog_capture = &vt1718S_pcm_analog_capture; 4782eb7188caSLydia Wang 4783bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060441) 4784bb3c6bfcSLydia Wang spec->stream_name_digital = "VT2020 Digital"; 4785bb3c6bfcSLydia Wang else if (codec->vendor_id == 0x11064441) 4786bb3c6bfcSLydia Wang spec->stream_name_digital = "VT1828S Digital"; 4787bb3c6bfcSLydia Wang else 4788eb7188caSLydia Wang spec->stream_name_digital = "VT1718S Digital"; 4789eb7188caSLydia Wang spec->stream_digital_playback = &vt1718S_pcm_digital_playback; 4790bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) 4791eb7188caSLydia Wang spec->stream_digital_capture = &vt1718S_pcm_digital_capture; 4792eb7188caSLydia Wang 4793eb7188caSLydia Wang if (!spec->adc_nids && spec->input_mux) { 4794eb7188caSLydia Wang spec->adc_nids = vt1718S_adc_nids; 4795eb7188caSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids); 4796eb7188caSLydia Wang get_mux_nids(codec); 4797bb3c6bfcSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 4798bb3c6bfcSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 4799eb7188caSLydia Wang spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; 4800eb7188caSLydia Wang spec->num_mixers++; 4801eb7188caSLydia Wang } 4802eb7188caSLydia Wang 4803eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 4804eb7188caSLydia Wang 4805eb7188caSLydia Wang codec->patch_ops.init = via_auto_init; 4806eb7188caSLydia Wang codec->patch_ops.unsol_event = via_unsol_event, 4807eb7188caSLydia Wang 4808eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4809eb7188caSLydia Wang spec->loopback.amplist = vt1718S_loopbacks; 4810eb7188caSLydia Wang #endif 4811eb7188caSLydia Wang 4812eb7188caSLydia Wang return 0; 4813eb7188caSLydia Wang } 4814f3db423dSLydia Wang 4815f3db423dSLydia Wang /* Patch for VT1716S */ 4816f3db423dSLydia Wang 4817f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 4818f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 4819f3db423dSLydia Wang { 4820f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 4821f3db423dSLydia Wang uinfo->count = 1; 4822f3db423dSLydia Wang uinfo->value.integer.min = 0; 4823f3db423dSLydia Wang uinfo->value.integer.max = 1; 4824f3db423dSLydia Wang return 0; 4825f3db423dSLydia Wang } 4826f3db423dSLydia Wang 4827f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 4828f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 4829f3db423dSLydia Wang { 4830f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4831f3db423dSLydia Wang int index = 0; 4832f3db423dSLydia Wang 4833f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 4834f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 4835f3db423dSLydia Wang if (index != -1) 4836f3db423dSLydia Wang *ucontrol->value.integer.value = index; 4837f3db423dSLydia Wang 4838f3db423dSLydia Wang return 0; 4839f3db423dSLydia Wang } 4840f3db423dSLydia Wang 4841f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 4842f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 4843f3db423dSLydia Wang { 4844f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4845f3db423dSLydia Wang struct via_spec *spec = codec->spec; 4846f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 4847f3db423dSLydia Wang 4848f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 4849f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 4850f3db423dSLydia Wang spec->dmic_enabled = index; 4851f3db423dSLydia Wang set_jack_power_state(codec); 4852f3db423dSLydia Wang 4853f3db423dSLydia Wang return 1; 4854f3db423dSLydia Wang } 4855f3db423dSLydia Wang 4856f3db423dSLydia Wang /* capture mixer elements */ 4857f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_capture_mixer[] = { 4858f3db423dSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 4859f3db423dSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 4860f3db423dSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 4861f3db423dSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 4862f3db423dSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 4863f3db423dSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 4864f3db423dSLydia Wang HDA_INPUT), 4865f3db423dSLydia Wang { 4866f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4867f3db423dSLydia Wang .name = "Input Source", 4868f3db423dSLydia Wang .count = 1, 4869f3db423dSLydia Wang .info = via_mux_enum_info, 4870f3db423dSLydia Wang .get = via_mux_enum_get, 4871f3db423dSLydia Wang .put = via_mux_enum_put, 4872f3db423dSLydia Wang }, 4873f3db423dSLydia Wang { } /* end */ 4874f3db423dSLydia Wang }; 4875f3db423dSLydia Wang 4876f3db423dSLydia Wang static struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 4877f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 4878f3db423dSLydia Wang { 4879f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4880f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 4881f3db423dSLydia Wang .count = 1, 4882f3db423dSLydia Wang .info = vt1716s_dmic_info, 4883f3db423dSLydia Wang .get = vt1716s_dmic_get, 4884f3db423dSLydia Wang .put = vt1716s_dmic_put, 4885f3db423dSLydia Wang }, 4886f3db423dSLydia Wang {} /* end */ 4887f3db423dSLydia Wang }; 4888f3db423dSLydia Wang 4889f3db423dSLydia Wang 4890f3db423dSLydia Wang /* mono-out mixer elements */ 4891f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 4892f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 4893f3db423dSLydia Wang { } /* end */ 4894f3db423dSLydia Wang }; 4895f3db423dSLydia Wang 4896f3db423dSLydia Wang static struct hda_verb vt1716S_volume_init_verbs[] = { 4897f3db423dSLydia Wang /* 4898f3db423dSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4899f3db423dSLydia Wang */ 4900f3db423dSLydia Wang {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4901f3db423dSLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4902f3db423dSLydia Wang 4903f3db423dSLydia Wang 4904f3db423dSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4905f3db423dSLydia Wang * mixer widget 4906f3db423dSLydia Wang */ 4907f3db423dSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4908f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4909f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4910f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4911f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4912f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4913f3db423dSLydia Wang 4914f3db423dSLydia Wang /* MUX Indices: Stereo Mixer = 5 */ 4915f3db423dSLydia Wang {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, 4916f3db423dSLydia Wang 4917f3db423dSLydia Wang /* Setup default input of PW4 to MW0 */ 4918f3db423dSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 4919f3db423dSLydia Wang 4920f3db423dSLydia Wang /* Setup default input of SW1 as MW0 */ 4921f3db423dSLydia Wang {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, 4922f3db423dSLydia Wang 4923f3db423dSLydia Wang /* Setup default input of SW4 as AOW0 */ 4924f3db423dSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 4925f3db423dSLydia Wang 4926f3db423dSLydia Wang /* PW9 PW10 Output enable */ 4927f3db423dSLydia Wang {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4928f3db423dSLydia Wang {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4929f3db423dSLydia Wang 4930f3db423dSLydia Wang /* Unmute SW1, PW12 */ 4931f3db423dSLydia Wang {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4932f3db423dSLydia Wang {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 4933f3db423dSLydia Wang /* PW12 Output enable */ 4934f3db423dSLydia Wang {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4935f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 4936f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 4937f3db423dSLydia Wang /* don't bybass mixer */ 4938f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 4939f3db423dSLydia Wang /* Enable mono output */ 4940f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 4941f3db423dSLydia Wang { } 4942f3db423dSLydia Wang }; 4943f3db423dSLydia Wang 4944f3db423dSLydia Wang 4945f3db423dSLydia Wang static struct hda_verb vt1716S_uniwill_init_verbs[] = { 4946f3db423dSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 4947f3db423dSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4948f3db423dSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4949f3db423dSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4950f3db423dSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4951f3db423dSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 4952f3db423dSLydia Wang AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, 4953f3db423dSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4954f3db423dSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4955f3db423dSLydia Wang { } 4956f3db423dSLydia Wang }; 4957f3db423dSLydia Wang 4958f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_playback = { 4959f3db423dSLydia Wang .substreams = 2, 4960f3db423dSLydia Wang .channels_min = 2, 4961f3db423dSLydia Wang .channels_max = 6, 4962f3db423dSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 4963f3db423dSLydia Wang .ops = { 4964f3db423dSLydia Wang .open = via_playback_pcm_open, 4965f3db423dSLydia Wang .prepare = via_playback_multi_pcm_prepare, 4966f3db423dSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 4967f3db423dSLydia Wang .close = via_pcm_open_close, 4968f3db423dSLydia Wang }, 4969f3db423dSLydia Wang }; 4970f3db423dSLydia Wang 4971f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_capture = { 4972f3db423dSLydia Wang .substreams = 2, 4973f3db423dSLydia Wang .channels_min = 2, 4974f3db423dSLydia Wang .channels_max = 2, 4975f3db423dSLydia Wang .nid = 0x13, /* NID to query formats and rates */ 4976f3db423dSLydia Wang .ops = { 4977f3db423dSLydia Wang .open = via_pcm_open_close, 4978f3db423dSLydia Wang .prepare = via_capture_pcm_prepare, 4979f3db423dSLydia Wang .cleanup = via_capture_pcm_cleanup, 4980f3db423dSLydia Wang .close = via_pcm_open_close, 4981f3db423dSLydia Wang }, 4982f3db423dSLydia Wang }; 4983f3db423dSLydia Wang 4984f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_digital_playback = { 4985f3db423dSLydia Wang .substreams = 2, 4986f3db423dSLydia Wang .channels_min = 2, 4987f3db423dSLydia Wang .channels_max = 2, 4988f3db423dSLydia Wang .rates = SNDRV_PCM_RATE_48000, 4989f3db423dSLydia Wang /* NID is set in via_build_pcms */ 4990f3db423dSLydia Wang .ops = { 4991f3db423dSLydia Wang .open = via_dig_playback_pcm_open, 4992f3db423dSLydia Wang .close = via_dig_playback_pcm_close, 4993f3db423dSLydia Wang .prepare = via_dig_playback_pcm_prepare, 4994f3db423dSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 4995f3db423dSLydia Wang }, 4996f3db423dSLydia Wang }; 4997f3db423dSLydia Wang 4998f3db423dSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 4999f3db423dSLydia Wang static int vt1716S_auto_fill_dac_nids(struct via_spec *spec, 5000f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5001f3db423dSLydia Wang { int i; 5002f3db423dSLydia Wang hda_nid_t nid; 5003f3db423dSLydia Wang 5004f3db423dSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 5005f3db423dSLydia Wang 5006f3db423dSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 5007f3db423dSLydia Wang 5008f3db423dSLydia Wang for (i = 0; i < 3; i++) { 5009f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 5010f3db423dSLydia Wang if (nid) { 5011f3db423dSLydia Wang /* config dac list */ 5012f3db423dSLydia Wang switch (i) { 5013f3db423dSLydia Wang case AUTO_SEQ_FRONT: 5014f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x10; 5015f3db423dSLydia Wang break; 5016f3db423dSLydia Wang case AUTO_SEQ_CENLFE: 5017f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x25; 5018f3db423dSLydia Wang break; 5019f3db423dSLydia Wang case AUTO_SEQ_SURROUND: 5020f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x11; 5021f3db423dSLydia Wang break; 5022f3db423dSLydia Wang } 5023f3db423dSLydia Wang } 5024f3db423dSLydia Wang } 5025f3db423dSLydia Wang 5026f3db423dSLydia Wang return 0; 5027f3db423dSLydia Wang } 5028f3db423dSLydia Wang 5029f3db423dSLydia Wang /* add playback controls from the parsed DAC table */ 5030f3db423dSLydia Wang static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec, 5031f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5032f3db423dSLydia Wang { 5033f3db423dSLydia Wang char name[32]; 5034f3db423dSLydia Wang static const char *chname[3] = { "Front", "Surround", "C/LFE" }; 5035f3db423dSLydia Wang hda_nid_t nid_vols[] = {0x10, 0x11, 0x25}; 5036f3db423dSLydia Wang hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27}; 5037f3db423dSLydia Wang hda_nid_t nid, nid_vol, nid_mute; 5038f3db423dSLydia Wang int i, err; 5039f3db423dSLydia Wang 5040f3db423dSLydia Wang for (i = 0; i <= AUTO_SEQ_CENLFE; i++) { 5041f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 5042f3db423dSLydia Wang 5043f3db423dSLydia Wang if (!nid) 5044f3db423dSLydia Wang continue; 5045f3db423dSLydia Wang 5046f3db423dSLydia Wang nid_vol = nid_vols[i]; 5047f3db423dSLydia Wang nid_mute = nid_mutes[i]; 5048f3db423dSLydia Wang 5049f3db423dSLydia Wang if (i == AUTO_SEQ_CENLFE) { 5050f3db423dSLydia Wang err = via_add_control( 5051f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 5052f3db423dSLydia Wang "Center Playback Volume", 5053f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); 5054f3db423dSLydia Wang if (err < 0) 5055f3db423dSLydia Wang return err; 5056f3db423dSLydia Wang err = via_add_control( 5057f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 5058f3db423dSLydia Wang "LFE Playback Volume", 5059f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); 5060f3db423dSLydia Wang if (err < 0) 5061f3db423dSLydia Wang return err; 5062f3db423dSLydia Wang err = via_add_control( 5063f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 5064f3db423dSLydia Wang "Center Playback Switch", 5065f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 5066f3db423dSLydia Wang HDA_OUTPUT)); 5067f3db423dSLydia Wang if (err < 0) 5068f3db423dSLydia Wang return err; 5069f3db423dSLydia Wang err = via_add_control( 5070f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 5071f3db423dSLydia Wang "LFE Playback Switch", 5072f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 5073f3db423dSLydia Wang HDA_OUTPUT)); 5074f3db423dSLydia Wang if (err < 0) 5075f3db423dSLydia Wang return err; 5076f3db423dSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 5077f3db423dSLydia Wang 5078f3db423dSLydia Wang err = via_add_control( 5079f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 5080f3db423dSLydia Wang "Master Front Playback Volume", 5081f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 5082f3db423dSLydia Wang if (err < 0) 5083f3db423dSLydia Wang return err; 5084f3db423dSLydia Wang err = via_add_control( 5085f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 5086f3db423dSLydia Wang "Master Front Playback Switch", 5087f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 5088f3db423dSLydia Wang if (err < 0) 5089f3db423dSLydia Wang return err; 5090f3db423dSLydia Wang 5091f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 5092f3db423dSLydia Wang err = via_add_control( 5093f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 5094f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 5095f3db423dSLydia Wang if (err < 0) 5096f3db423dSLydia Wang return err; 5097f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 5098f3db423dSLydia Wang err = via_add_control( 5099f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 5100f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 5101f3db423dSLydia Wang HDA_OUTPUT)); 5102f3db423dSLydia Wang if (err < 0) 5103f3db423dSLydia Wang return err; 5104f3db423dSLydia Wang } else { 5105f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 5106f3db423dSLydia Wang err = via_add_control( 5107f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 5108f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 5109f3db423dSLydia Wang if (err < 0) 5110f3db423dSLydia Wang return err; 5111f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 5112f3db423dSLydia Wang err = via_add_control( 5113f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 5114f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 5115f3db423dSLydia Wang HDA_OUTPUT)); 5116f3db423dSLydia Wang if (err < 0) 5117f3db423dSLydia Wang return err; 5118f3db423dSLydia Wang } 5119f3db423dSLydia Wang } 5120f3db423dSLydia Wang return 0; 5121f3db423dSLydia Wang } 5122f3db423dSLydia Wang 5123f3db423dSLydia Wang static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 5124f3db423dSLydia Wang { 5125f3db423dSLydia Wang int err; 5126f3db423dSLydia Wang 5127f3db423dSLydia Wang if (!pin) 5128f3db423dSLydia Wang return 0; 5129f3db423dSLydia Wang 5130f3db423dSLydia Wang spec->multiout.hp_nid = 0x25; /* AOW3 */ 5131f3db423dSLydia Wang spec->hp_independent_mode_index = 1; 5132f3db423dSLydia Wang 5133f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 5134f3db423dSLydia Wang "Headphone Playback Volume", 5135f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 5136f3db423dSLydia Wang if (err < 0) 5137f3db423dSLydia Wang return err; 5138f3db423dSLydia Wang 5139f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 5140f3db423dSLydia Wang "Headphone Playback Switch", 5141f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 5142f3db423dSLydia Wang if (err < 0) 5143f3db423dSLydia Wang return err; 5144f3db423dSLydia Wang 5145f3db423dSLydia Wang create_hp_imux(spec); 5146f3db423dSLydia Wang return 0; 5147f3db423dSLydia Wang } 5148f3db423dSLydia Wang 5149f3db423dSLydia Wang /* create playback/capture controls for input pins */ 5150f3db423dSLydia Wang static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec, 5151f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5152f3db423dSLydia Wang { 5153f3db423dSLydia Wang static char *labels[] = { 5154f3db423dSLydia Wang "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 5155f3db423dSLydia Wang }; 5156f3db423dSLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 5157f3db423dSLydia Wang int i, err, idx = 0; 5158f3db423dSLydia Wang 5159f3db423dSLydia Wang /* for internal loopback recording select */ 5160f3db423dSLydia Wang imux->items[imux->num_items].label = "Stereo Mixer"; 5161f3db423dSLydia Wang imux->items[imux->num_items].index = 5; 5162f3db423dSLydia Wang imux->num_items++; 5163f3db423dSLydia Wang 5164f3db423dSLydia Wang for (i = 0; i < AUTO_PIN_LAST; i++) { 5165f3db423dSLydia Wang if (!cfg->input_pins[i]) 5166f3db423dSLydia Wang continue; 5167f3db423dSLydia Wang 5168f3db423dSLydia Wang switch (cfg->input_pins[i]) { 5169f3db423dSLydia Wang case 0x1a: /* Mic */ 5170f3db423dSLydia Wang idx = 2; 5171f3db423dSLydia Wang break; 5172f3db423dSLydia Wang 5173f3db423dSLydia Wang case 0x1b: /* Line In */ 5174f3db423dSLydia Wang idx = 3; 5175f3db423dSLydia Wang break; 5176f3db423dSLydia Wang 5177f3db423dSLydia Wang case 0x1e: /* Front Mic */ 5178f3db423dSLydia Wang idx = 4; 5179f3db423dSLydia Wang break; 5180f3db423dSLydia Wang 5181f3db423dSLydia Wang case 0x1f: /* CD */ 5182f3db423dSLydia Wang idx = 1; 5183f3db423dSLydia Wang break; 5184f3db423dSLydia Wang } 5185f3db423dSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x16); 5186f3db423dSLydia Wang if (err < 0) 5187f3db423dSLydia Wang return err; 5188f3db423dSLydia Wang imux->items[imux->num_items].label = labels[i]; 5189f3db423dSLydia Wang imux->items[imux->num_items].index = idx-1; 5190f3db423dSLydia Wang imux->num_items++; 5191f3db423dSLydia Wang } 5192f3db423dSLydia Wang return 0; 5193f3db423dSLydia Wang } 5194f3db423dSLydia Wang 5195f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec) 5196f3db423dSLydia Wang { 5197f3db423dSLydia Wang struct via_spec *spec = codec->spec; 5198f3db423dSLydia Wang int err; 5199f3db423dSLydia Wang 5200f3db423dSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 5201f3db423dSLydia Wang if (err < 0) 5202f3db423dSLydia Wang return err; 5203f3db423dSLydia Wang err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg); 5204f3db423dSLydia Wang if (err < 0) 5205f3db423dSLydia Wang return err; 5206f3db423dSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 5207f3db423dSLydia Wang return 0; /* can't find valid BIOS pin config */ 5208f3db423dSLydia Wang 5209f3db423dSLydia Wang err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg); 5210f3db423dSLydia Wang if (err < 0) 5211f3db423dSLydia Wang return err; 5212f3db423dSLydia Wang err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 5213f3db423dSLydia Wang if (err < 0) 5214f3db423dSLydia Wang return err; 5215f3db423dSLydia Wang err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg); 5216f3db423dSLydia Wang if (err < 0) 5217f3db423dSLydia Wang return err; 5218f3db423dSLydia Wang 5219f3db423dSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 5220f3db423dSLydia Wang 5221f3db423dSLydia Wang fill_dig_outs(codec); 5222f3db423dSLydia Wang 5223f3db423dSLydia Wang if (spec->kctls.list) 5224f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 5225f3db423dSLydia Wang 5226f3db423dSLydia Wang spec->input_mux = &spec->private_imux[0]; 5227f3db423dSLydia Wang 5228f3db423dSLydia Wang if (spec->hp_mux) 5229f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = via_hp_mixer; 5230f3db423dSLydia Wang 5231f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = via_smart51_mixer; 5232f3db423dSLydia Wang 5233f3db423dSLydia Wang return 1; 5234f3db423dSLydia Wang } 5235f3db423dSLydia Wang 5236f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5237f3db423dSLydia Wang static struct hda_amp_list vt1716S_loopbacks[] = { 5238f3db423dSLydia Wang { 0x16, HDA_INPUT, 1 }, 5239f3db423dSLydia Wang { 0x16, HDA_INPUT, 2 }, 5240f3db423dSLydia Wang { 0x16, HDA_INPUT, 3 }, 5241f3db423dSLydia Wang { 0x16, HDA_INPUT, 4 }, 5242f3db423dSLydia Wang { } /* end */ 5243f3db423dSLydia Wang }; 5244f3db423dSLydia Wang #endif 5245f3db423dSLydia Wang 5246f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 5247f3db423dSLydia Wang { 5248f3db423dSLydia Wang struct via_spec *spec; 5249f3db423dSLydia Wang int err; 5250f3db423dSLydia Wang 5251f3db423dSLydia Wang /* create a codec specific record */ 5252f3db423dSLydia Wang spec = kzalloc(sizeof(*spec), GFP_KERNEL); 5253f3db423dSLydia Wang if (spec == NULL) 5254f3db423dSLydia Wang return -ENOMEM; 5255f3db423dSLydia Wang 5256f3db423dSLydia Wang codec->spec = spec; 5257f3db423dSLydia Wang 5258f3db423dSLydia Wang /* automatic parse from the BIOS config */ 5259f3db423dSLydia Wang err = vt1716S_parse_auto_config(codec); 5260f3db423dSLydia Wang if (err < 0) { 5261f3db423dSLydia Wang via_free(codec); 5262f3db423dSLydia Wang return err; 5263f3db423dSLydia Wang } else if (!err) { 5264f3db423dSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 5265f3db423dSLydia Wang "from BIOS. Using genenic mode...\n"); 5266f3db423dSLydia Wang } 5267f3db423dSLydia Wang 5268f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; 5269f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; 5270f3db423dSLydia Wang 5271f3db423dSLydia Wang spec->stream_name_analog = "VT1716S Analog"; 5272f3db423dSLydia Wang spec->stream_analog_playback = &vt1716S_pcm_analog_playback; 5273f3db423dSLydia Wang spec->stream_analog_capture = &vt1716S_pcm_analog_capture; 5274f3db423dSLydia Wang 5275f3db423dSLydia Wang spec->stream_name_digital = "VT1716S Digital"; 5276f3db423dSLydia Wang spec->stream_digital_playback = &vt1716S_pcm_digital_playback; 5277f3db423dSLydia Wang 5278f3db423dSLydia Wang if (!spec->adc_nids && spec->input_mux) { 5279f3db423dSLydia Wang spec->adc_nids = vt1716S_adc_nids; 5280f3db423dSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids); 5281f3db423dSLydia Wang get_mux_nids(codec); 5282f3db423dSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 5283f3db423dSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 5284f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; 5285f3db423dSLydia Wang spec->num_mixers++; 5286f3db423dSLydia Wang } 5287f3db423dSLydia Wang 5288f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 5289f3db423dSLydia Wang spec->num_mixers++; 5290f3db423dSLydia Wang 5291f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 5292f3db423dSLydia Wang 5293f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 5294f3db423dSLydia Wang 5295f3db423dSLydia Wang codec->patch_ops.init = via_auto_init; 5296f3db423dSLydia Wang codec->patch_ops.unsol_event = via_unsol_event, 5297f3db423dSLydia Wang 5298f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5299f3db423dSLydia Wang spec->loopback.amplist = vt1716S_loopbacks; 5300f3db423dSLydia Wang #endif 5301f3db423dSLydia Wang 5302f3db423dSLydia Wang return 0; 5303f3db423dSLydia Wang } 5304*25eaba2fSLydia Wang 5305*25eaba2fSLydia Wang /* for vt2002P */ 5306*25eaba2fSLydia Wang 5307*25eaba2fSLydia Wang /* capture mixer elements */ 5308*25eaba2fSLydia Wang static struct snd_kcontrol_new vt2002P_capture_mixer[] = { 5309*25eaba2fSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 5310*25eaba2fSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 5311*25eaba2fSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 5312*25eaba2fSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 5313*25eaba2fSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 5314*25eaba2fSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 5315*25eaba2fSLydia Wang HDA_INPUT), 5316*25eaba2fSLydia Wang { 5317*25eaba2fSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5318*25eaba2fSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 5319*25eaba2fSLydia Wang * So call somewhat different.. 5320*25eaba2fSLydia Wang */ 5321*25eaba2fSLydia Wang /* .name = "Capture Source", */ 5322*25eaba2fSLydia Wang .name = "Input Source", 5323*25eaba2fSLydia Wang .count = 2, 5324*25eaba2fSLydia Wang .info = via_mux_enum_info, 5325*25eaba2fSLydia Wang .get = via_mux_enum_get, 5326*25eaba2fSLydia Wang .put = via_mux_enum_put, 5327*25eaba2fSLydia Wang }, 5328*25eaba2fSLydia Wang { } /* end */ 5329*25eaba2fSLydia Wang }; 5330*25eaba2fSLydia Wang 5331*25eaba2fSLydia Wang static struct hda_verb vt2002P_volume_init_verbs[] = { 5332*25eaba2fSLydia Wang /* 5333*25eaba2fSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 5334*25eaba2fSLydia Wang */ 5335*25eaba2fSLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5336*25eaba2fSLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5337*25eaba2fSLydia Wang 5338*25eaba2fSLydia Wang 5339*25eaba2fSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 5340*25eaba2fSLydia Wang * mixer widget 5341*25eaba2fSLydia Wang */ 5342*25eaba2fSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 5343*25eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 5344*25eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 5345*25eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 5346*25eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 5347*25eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 5348*25eaba2fSLydia Wang 5349*25eaba2fSLydia Wang /* MUX Indices: Mic = 0 */ 5350*25eaba2fSLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 5351*25eaba2fSLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 5352*25eaba2fSLydia Wang 5353*25eaba2fSLydia Wang /* PW9 Output enable */ 5354*25eaba2fSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 5355*25eaba2fSLydia Wang 5356*25eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 5357*25eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 5358*25eaba2fSLydia Wang 5359*25eaba2fSLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 5360*25eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5361*25eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5362*25eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5363*25eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5364*25eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5365*25eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5366*25eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5367*25eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5368*25eaba2fSLydia Wang 5369*25eaba2fSLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 5370*25eaba2fSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 5371*25eaba2fSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 5372*25eaba2fSLydia Wang {0x37, AC_VERB_SET_CONNECT_SEL, 0}, 5373*25eaba2fSLydia Wang {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, 5374*25eaba2fSLydia Wang 5375*25eaba2fSLydia Wang /* set PW0 index=0 (MW0) */ 5376*25eaba2fSLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 5377*25eaba2fSLydia Wang 5378*25eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 5379*25eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 5380*25eaba2fSLydia Wang { } 5381*25eaba2fSLydia Wang }; 5382*25eaba2fSLydia Wang 5383*25eaba2fSLydia Wang 5384*25eaba2fSLydia Wang static struct hda_verb vt2002P_uniwill_init_verbs[] = { 5385*25eaba2fSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 5386*25eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5387*25eaba2fSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, 5388*25eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5389*25eaba2fSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5390*25eaba2fSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5391*25eaba2fSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5392*25eaba2fSLydia Wang { } 5393*25eaba2fSLydia Wang }; 5394*25eaba2fSLydia Wang 5395*25eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_playback = { 5396*25eaba2fSLydia Wang .substreams = 2, 5397*25eaba2fSLydia Wang .channels_min = 2, 5398*25eaba2fSLydia Wang .channels_max = 2, 5399*25eaba2fSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 5400*25eaba2fSLydia Wang .ops = { 5401*25eaba2fSLydia Wang .open = via_playback_pcm_open, 5402*25eaba2fSLydia Wang .prepare = via_playback_multi_pcm_prepare, 5403*25eaba2fSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 5404*25eaba2fSLydia Wang .close = via_pcm_open_close, 5405*25eaba2fSLydia Wang }, 5406*25eaba2fSLydia Wang }; 5407*25eaba2fSLydia Wang 5408*25eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_capture = { 5409*25eaba2fSLydia Wang .substreams = 2, 5410*25eaba2fSLydia Wang .channels_min = 2, 5411*25eaba2fSLydia Wang .channels_max = 2, 5412*25eaba2fSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 5413*25eaba2fSLydia Wang .ops = { 5414*25eaba2fSLydia Wang .open = via_pcm_open_close, 5415*25eaba2fSLydia Wang .prepare = via_capture_pcm_prepare, 5416*25eaba2fSLydia Wang .cleanup = via_capture_pcm_cleanup, 5417*25eaba2fSLydia Wang .close = via_pcm_open_close, 5418*25eaba2fSLydia Wang }, 5419*25eaba2fSLydia Wang }; 5420*25eaba2fSLydia Wang 5421*25eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_digital_playback = { 5422*25eaba2fSLydia Wang .substreams = 1, 5423*25eaba2fSLydia Wang .channels_min = 2, 5424*25eaba2fSLydia Wang .channels_max = 2, 5425*25eaba2fSLydia Wang .rates = SNDRV_PCM_RATE_48000, 5426*25eaba2fSLydia Wang /* NID is set in via_build_pcms */ 5427*25eaba2fSLydia Wang .ops = { 5428*25eaba2fSLydia Wang .open = via_dig_playback_pcm_open, 5429*25eaba2fSLydia Wang .close = via_dig_playback_pcm_close, 5430*25eaba2fSLydia Wang .prepare = via_dig_playback_pcm_prepare, 5431*25eaba2fSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 5432*25eaba2fSLydia Wang }, 5433*25eaba2fSLydia Wang }; 5434*25eaba2fSLydia Wang 5435*25eaba2fSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 5436*25eaba2fSLydia Wang static int vt2002P_auto_fill_dac_nids(struct via_spec *spec, 5437*25eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 5438*25eaba2fSLydia Wang { 5439*25eaba2fSLydia Wang spec->multiout.num_dacs = 1; 5440*25eaba2fSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 5441*25eaba2fSLydia Wang if (cfg->line_out_pins[0]) 5442*25eaba2fSLydia Wang spec->multiout.dac_nids[0] = 0x8; 5443*25eaba2fSLydia Wang return 0; 5444*25eaba2fSLydia Wang } 5445*25eaba2fSLydia Wang 5446*25eaba2fSLydia Wang /* add playback controls from the parsed DAC table */ 5447*25eaba2fSLydia Wang static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec, 5448*25eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 5449*25eaba2fSLydia Wang { 5450*25eaba2fSLydia Wang int err; 5451*25eaba2fSLydia Wang 5452*25eaba2fSLydia Wang if (!cfg->line_out_pins[0]) 5453*25eaba2fSLydia Wang return -1; 5454*25eaba2fSLydia Wang 5455*25eaba2fSLydia Wang 5456*25eaba2fSLydia Wang /* Line-Out: PortE */ 5457*25eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 5458*25eaba2fSLydia Wang "Master Front Playback Volume", 5459*25eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); 5460*25eaba2fSLydia Wang if (err < 0) 5461*25eaba2fSLydia Wang return err; 5462*25eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, 5463*25eaba2fSLydia Wang "Master Front Playback Switch", 5464*25eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT)); 5465*25eaba2fSLydia Wang if (err < 0) 5466*25eaba2fSLydia Wang return err; 5467*25eaba2fSLydia Wang 5468*25eaba2fSLydia Wang return 0; 5469*25eaba2fSLydia Wang } 5470*25eaba2fSLydia Wang 5471*25eaba2fSLydia Wang static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 5472*25eaba2fSLydia Wang { 5473*25eaba2fSLydia Wang int err; 5474*25eaba2fSLydia Wang 5475*25eaba2fSLydia Wang if (!pin) 5476*25eaba2fSLydia Wang return 0; 5477*25eaba2fSLydia Wang 5478*25eaba2fSLydia Wang spec->multiout.hp_nid = 0x9; 5479*25eaba2fSLydia Wang spec->hp_independent_mode_index = 1; 5480*25eaba2fSLydia Wang 5481*25eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 5482*25eaba2fSLydia Wang "Headphone Playback Volume", 5483*25eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL( 5484*25eaba2fSLydia Wang spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); 5485*25eaba2fSLydia Wang if (err < 0) 5486*25eaba2fSLydia Wang return err; 5487*25eaba2fSLydia Wang 5488*25eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 5489*25eaba2fSLydia Wang "Headphone Playback Switch", 5490*25eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 5491*25eaba2fSLydia Wang if (err < 0) 5492*25eaba2fSLydia Wang return err; 5493*25eaba2fSLydia Wang 5494*25eaba2fSLydia Wang create_hp_imux(spec); 5495*25eaba2fSLydia Wang return 0; 5496*25eaba2fSLydia Wang } 5497*25eaba2fSLydia Wang 5498*25eaba2fSLydia Wang /* create playback/capture controls for input pins */ 5499*25eaba2fSLydia Wang static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec, 5500*25eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 5501*25eaba2fSLydia Wang { 5502*25eaba2fSLydia Wang static char *labels[] = { 5503*25eaba2fSLydia Wang "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 5504*25eaba2fSLydia Wang }; 5505*25eaba2fSLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 5506*25eaba2fSLydia Wang int i, err, idx = 0; 5507*25eaba2fSLydia Wang 5508*25eaba2fSLydia Wang for (i = 0; i < AUTO_PIN_LAST; i++) { 5509*25eaba2fSLydia Wang if (!cfg->input_pins[i]) 5510*25eaba2fSLydia Wang continue; 5511*25eaba2fSLydia Wang 5512*25eaba2fSLydia Wang switch (cfg->input_pins[i]) { 5513*25eaba2fSLydia Wang case 0x2b: /* Mic */ 5514*25eaba2fSLydia Wang idx = 0; 5515*25eaba2fSLydia Wang break; 5516*25eaba2fSLydia Wang 5517*25eaba2fSLydia Wang case 0x2a: /* Line In */ 5518*25eaba2fSLydia Wang idx = 1; 5519*25eaba2fSLydia Wang break; 5520*25eaba2fSLydia Wang 5521*25eaba2fSLydia Wang case 0x29: /* Front Mic */ 5522*25eaba2fSLydia Wang idx = 2; 5523*25eaba2fSLydia Wang break; 5524*25eaba2fSLydia Wang } 5525*25eaba2fSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x21); 5526*25eaba2fSLydia Wang if (err < 0) 5527*25eaba2fSLydia Wang return err; 5528*25eaba2fSLydia Wang imux->items[imux->num_items].label = labels[i]; 5529*25eaba2fSLydia Wang imux->items[imux->num_items].index = idx; 5530*25eaba2fSLydia Wang imux->num_items++; 5531*25eaba2fSLydia Wang } 5532*25eaba2fSLydia Wang 5533*25eaba2fSLydia Wang /* build volume/mute control of loopback */ 5534*25eaba2fSLydia Wang err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21); 5535*25eaba2fSLydia Wang if (err < 0) 5536*25eaba2fSLydia Wang return err; 5537*25eaba2fSLydia Wang 5538*25eaba2fSLydia Wang /* for internal loopback recording select */ 5539*25eaba2fSLydia Wang imux->items[imux->num_items].label = "Stereo Mixer"; 5540*25eaba2fSLydia Wang imux->items[imux->num_items].index = 3; 5541*25eaba2fSLydia Wang imux->num_items++; 5542*25eaba2fSLydia Wang 5543*25eaba2fSLydia Wang /* for digital mic select */ 5544*25eaba2fSLydia Wang imux->items[imux->num_items].label = "Digital Mic"; 5545*25eaba2fSLydia Wang imux->items[imux->num_items].index = 4; 5546*25eaba2fSLydia Wang imux->num_items++; 5547*25eaba2fSLydia Wang 5548*25eaba2fSLydia Wang return 0; 5549*25eaba2fSLydia Wang } 5550*25eaba2fSLydia Wang 5551*25eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec) 5552*25eaba2fSLydia Wang { 5553*25eaba2fSLydia Wang struct via_spec *spec = codec->spec; 5554*25eaba2fSLydia Wang int err; 5555*25eaba2fSLydia Wang 5556*25eaba2fSLydia Wang 5557*25eaba2fSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 5558*25eaba2fSLydia Wang if (err < 0) 5559*25eaba2fSLydia Wang return err; 5560*25eaba2fSLydia Wang 5561*25eaba2fSLydia Wang err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg); 5562*25eaba2fSLydia Wang if (err < 0) 5563*25eaba2fSLydia Wang return err; 5564*25eaba2fSLydia Wang 5565*25eaba2fSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 5566*25eaba2fSLydia Wang return 0; /* can't find valid BIOS pin config */ 5567*25eaba2fSLydia Wang 5568*25eaba2fSLydia Wang err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg); 5569*25eaba2fSLydia Wang if (err < 0) 5570*25eaba2fSLydia Wang return err; 5571*25eaba2fSLydia Wang err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 5572*25eaba2fSLydia Wang if (err < 0) 5573*25eaba2fSLydia Wang return err; 5574*25eaba2fSLydia Wang err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg); 5575*25eaba2fSLydia Wang if (err < 0) 5576*25eaba2fSLydia Wang return err; 5577*25eaba2fSLydia Wang 5578*25eaba2fSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 5579*25eaba2fSLydia Wang 5580*25eaba2fSLydia Wang fill_dig_outs(codec); 5581*25eaba2fSLydia Wang 5582*25eaba2fSLydia Wang if (spec->kctls.list) 5583*25eaba2fSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 5584*25eaba2fSLydia Wang 5585*25eaba2fSLydia Wang spec->input_mux = &spec->private_imux[0]; 5586*25eaba2fSLydia Wang 5587*25eaba2fSLydia Wang if (spec->hp_mux) 5588*25eaba2fSLydia Wang spec->mixers[spec->num_mixers++] = via_hp_mixer; 5589*25eaba2fSLydia Wang 5590*25eaba2fSLydia Wang return 1; 5591*25eaba2fSLydia Wang } 5592*25eaba2fSLydia Wang 5593*25eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5594*25eaba2fSLydia Wang static struct hda_amp_list vt2002P_loopbacks[] = { 5595*25eaba2fSLydia Wang { 0x21, HDA_INPUT, 0 }, 5596*25eaba2fSLydia Wang { 0x21, HDA_INPUT, 1 }, 5597*25eaba2fSLydia Wang { 0x21, HDA_INPUT, 2 }, 5598*25eaba2fSLydia Wang { } /* end */ 5599*25eaba2fSLydia Wang }; 5600*25eaba2fSLydia Wang #endif 5601*25eaba2fSLydia Wang 5602*25eaba2fSLydia Wang 5603*25eaba2fSLydia Wang /* patch for vt2002P */ 5604*25eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 5605*25eaba2fSLydia Wang { 5606*25eaba2fSLydia Wang struct via_spec *spec; 5607*25eaba2fSLydia Wang int err; 5608*25eaba2fSLydia Wang 5609*25eaba2fSLydia Wang /* create a codec specific record */ 5610*25eaba2fSLydia Wang spec = kzalloc(sizeof(*spec), GFP_KERNEL); 5611*25eaba2fSLydia Wang if (spec == NULL) 5612*25eaba2fSLydia Wang return -ENOMEM; 5613*25eaba2fSLydia Wang 5614*25eaba2fSLydia Wang codec->spec = spec; 5615*25eaba2fSLydia Wang 5616*25eaba2fSLydia Wang /* automatic parse from the BIOS config */ 5617*25eaba2fSLydia Wang err = vt2002P_parse_auto_config(codec); 5618*25eaba2fSLydia Wang if (err < 0) { 5619*25eaba2fSLydia Wang via_free(codec); 5620*25eaba2fSLydia Wang return err; 5621*25eaba2fSLydia Wang } else if (!err) { 5622*25eaba2fSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 5623*25eaba2fSLydia Wang "from BIOS. Using genenic mode...\n"); 5624*25eaba2fSLydia Wang } 5625*25eaba2fSLydia Wang 5626*25eaba2fSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt2002P_volume_init_verbs; 5627*25eaba2fSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs; 5628*25eaba2fSLydia Wang 5629*25eaba2fSLydia Wang spec->stream_name_analog = "VT2002P Analog"; 5630*25eaba2fSLydia Wang spec->stream_analog_playback = &vt2002P_pcm_analog_playback; 5631*25eaba2fSLydia Wang spec->stream_analog_capture = &vt2002P_pcm_analog_capture; 5632*25eaba2fSLydia Wang 5633*25eaba2fSLydia Wang spec->stream_name_digital = "VT2002P Digital"; 5634*25eaba2fSLydia Wang spec->stream_digital_playback = &vt2002P_pcm_digital_playback; 5635*25eaba2fSLydia Wang 5636*25eaba2fSLydia Wang if (!spec->adc_nids && spec->input_mux) { 5637*25eaba2fSLydia Wang spec->adc_nids = vt2002P_adc_nids; 5638*25eaba2fSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids); 5639*25eaba2fSLydia Wang get_mux_nids(codec); 5640*25eaba2fSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 5641*25eaba2fSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 5642*25eaba2fSLydia Wang spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; 5643*25eaba2fSLydia Wang spec->num_mixers++; 5644*25eaba2fSLydia Wang } 5645*25eaba2fSLydia Wang 5646*25eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 5647*25eaba2fSLydia Wang 5648*25eaba2fSLydia Wang codec->patch_ops.init = via_auto_init; 5649*25eaba2fSLydia Wang codec->patch_ops.unsol_event = via_unsol_event, 5650*25eaba2fSLydia Wang 5651*25eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5652*25eaba2fSLydia Wang spec->loopback.amplist = vt2002P_loopbacks; 5653*25eaba2fSLydia Wang #endif 5654*25eaba2fSLydia Wang 5655*25eaba2fSLydia Wang return 0; 5656*25eaba2fSLydia Wang } 5657c577b8a1SJoseph Chan /* 5658c577b8a1SJoseph Chan * patch entries 5659c577b8a1SJoseph Chan */ 56601289e9e8STakashi Iwai static struct hda_codec_preset snd_hda_preset_via[] = { 56613218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 56623218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 56633218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 56643218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 56653218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 5666f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 56673218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 5668f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 56693218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 5670f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 56713218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 5672f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 56733218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 5674f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 56753218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 5676f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 56773218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 5678f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 56793218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 5680f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 56813218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 5682f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 56833218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 5684f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 56853218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 5686f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 56873218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 5688f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 56893218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 5690f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 56913218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 5692f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 56933218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 5694f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 56953218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 5696f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 56973218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 5698d949cac1SHarald Welte .patch = patch_vt1708S}, 56993218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 5700d949cac1SHarald Welte .patch = patch_vt1708S}, 57013218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 5702d949cac1SHarald Welte .patch = patch_vt1708S}, 57033218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 5704d949cac1SHarald Welte .patch = patch_vt1708S}, 57053218c178STakashi Iwai { .id = 0x11064397, .name = "VT1708S", 5706d949cac1SHarald Welte .patch = patch_vt1708S}, 57073218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 5708d949cac1SHarald Welte .patch = patch_vt1708S}, 57093218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 5710d949cac1SHarald Welte .patch = patch_vt1708S}, 57113218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 5712d949cac1SHarald Welte .patch = patch_vt1708S}, 57133218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 5714d949cac1SHarald Welte .patch = patch_vt1702}, 57153218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 5716d949cac1SHarald Welte .patch = patch_vt1702}, 57173218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 5718d949cac1SHarald Welte .patch = patch_vt1702}, 57193218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 5720d949cac1SHarald Welte .patch = patch_vt1702}, 57213218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 5722d949cac1SHarald Welte .patch = patch_vt1702}, 57233218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 5724d949cac1SHarald Welte .patch = patch_vt1702}, 57253218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 5726d949cac1SHarald Welte .patch = patch_vt1702}, 57273218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 5728d949cac1SHarald Welte .patch = patch_vt1702}, 5729eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 5730eb7188caSLydia Wang .patch = patch_vt1718S}, 5731eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 5732eb7188caSLydia Wang .patch = patch_vt1718S}, 5733bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 5734bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 5735bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 5736bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 5737f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 5738f3db423dSLydia Wang .patch = patch_vt1716S}, 5739f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 5740f3db423dSLydia Wang .patch = patch_vt1716S}, 5741*25eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 5742*25eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 5743c577b8a1SJoseph Chan {} /* terminator */ 5744c577b8a1SJoseph Chan }; 57451289e9e8STakashi Iwai 57461289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 57471289e9e8STakashi Iwai 57481289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 57491289e9e8STakashi Iwai .preset = snd_hda_preset_via, 57501289e9e8STakashi Iwai .owner = THIS_MODULE, 57511289e9e8STakashi Iwai }; 57521289e9e8STakashi Iwai 57531289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 57541289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 57551289e9e8STakashi Iwai 57561289e9e8STakashi Iwai static int __init patch_via_init(void) 57571289e9e8STakashi Iwai { 57581289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 57591289e9e8STakashi Iwai } 57601289e9e8STakashi Iwai 57611289e9e8STakashi Iwai static void __exit patch_via_exit(void) 57621289e9e8STakashi Iwai { 57631289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 57641289e9e8STakashi Iwai } 57651289e9e8STakashi Iwai 57661289e9e8STakashi Iwai module_init(patch_via_init) 57671289e9e8STakashi Iwai module_exit(patch_via_exit) 5768