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, 89d7426329SHarald Welte CODEC_TYPES, 90d7426329SHarald Welte }; 91d7426329SHarald Welte 92744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 93d7426329SHarald Welte { 94744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 95d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 96d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 97d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 98d7426329SHarald Welte 99d7426329SHarald Welte /* get codec type */ 100d7426329SHarald Welte if (ven_id != 0x1106) 101d7426329SHarald Welte codec_type = UNKNOWN; 102d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 103d7426329SHarald Welte codec_type = VT1708; 104d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 105d7426329SHarald Welte codec_type = VT1709_10CH; 106d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 107d7426329SHarald Welte codec_type = VT1709_6CH; 108518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 109d7426329SHarald Welte codec_type = VT1708B_8CH; 110518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 111518bf3baSLydia Wang codec_type = VT1708BCE; 112518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 113d7426329SHarald Welte codec_type = VT1708B_4CH; 114d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 115d7426329SHarald Welte && (dev_id >> 12) < 8) 116d7426329SHarald Welte codec_type = VT1708S; 117d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 118d7426329SHarald Welte && (dev_id >> 12) < 8) 119d7426329SHarald Welte codec_type = VT1702; 120d7426329SHarald Welte else 121d7426329SHarald Welte codec_type = UNKNOWN; 122d7426329SHarald Welte return codec_type; 123d7426329SHarald Welte }; 124d7426329SHarald Welte 12569e52a80SHarald Welte #define VIA_HP_EVENT 0x01 12669e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 12769e52a80SHarald Welte 128c577b8a1SJoseph Chan enum { 129c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 130c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 131f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 132c577b8a1SJoseph Chan }; 133c577b8a1SJoseph Chan 134c577b8a1SJoseph Chan enum { 135eb14a46cSHarald Welte AUTO_SEQ_FRONT = 0, 136c577b8a1SJoseph Chan AUTO_SEQ_SURROUND, 137c577b8a1SJoseph Chan AUTO_SEQ_CENLFE, 138c577b8a1SJoseph Chan AUTO_SEQ_SIDE 139c577b8a1SJoseph Chan }; 140c577b8a1SJoseph Chan 141d7426329SHarald Welte /* Some VT1708S based boards gets the micboost setting wrong, so we have 142d7426329SHarald Welte * to apply some brute-force and re-write the TLV's by software. */ 143d7426329SHarald Welte static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag, 144d7426329SHarald Welte unsigned int size, unsigned int __user *_tlv) 145d7426329SHarald Welte { 146d7426329SHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 147d7426329SHarald Welte hda_nid_t nid = get_amp_nid(kcontrol); 148d7426329SHarald Welte 149744ff5f4SLydia Wang if (get_codec_type(codec) == VT1708S 150d7426329SHarald Welte && (nid == 0x1a || nid == 0x1e)) { 151d7426329SHarald Welte if (size < 4 * sizeof(unsigned int)) 152d7426329SHarald Welte return -ENOMEM; 153d7426329SHarald Welte if (put_user(1, _tlv)) /* SNDRV_CTL_TLVT_DB_SCALE */ 154d7426329SHarald Welte return -EFAULT; 155d7426329SHarald Welte if (put_user(2 * sizeof(unsigned int), _tlv + 1)) 156d7426329SHarald Welte return -EFAULT; 157d7426329SHarald Welte if (put_user(0, _tlv + 2)) /* offset = 0 */ 158d7426329SHarald Welte return -EFAULT; 159d7426329SHarald Welte if (put_user(1000, _tlv + 3)) /* step size = 10 dB */ 160d7426329SHarald Welte return -EFAULT; 161d7426329SHarald Welte } 162d7426329SHarald Welte return 0; 163d7426329SHarald Welte } 164d7426329SHarald Welte 165d7426329SHarald Welte static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, 166d7426329SHarald Welte struct snd_ctl_elem_info *uinfo) 167d7426329SHarald Welte { 168d7426329SHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 169d7426329SHarald Welte hda_nid_t nid = get_amp_nid(kcontrol); 170d7426329SHarald Welte 171744ff5f4SLydia Wang if (get_codec_type(codec) == VT1708S 172d7426329SHarald Welte && (nid == 0x1a || nid == 0x1e)) { 173d7426329SHarald Welte uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 174d7426329SHarald Welte uinfo->count = 2; 175d7426329SHarald Welte uinfo->value.integer.min = 0; 176d7426329SHarald Welte uinfo->value.integer.max = 3; 177d7426329SHarald Welte } 178d7426329SHarald Welte return 0; 179d7426329SHarald Welte } 180d7426329SHarald Welte 181f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); 182f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec); 183f5271101SLydia Wang 184f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 185f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 186f5271101SLydia Wang { 187f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 188f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 189f5271101SLydia Wang 190f5271101SLydia Wang set_jack_power_state(codec); 191f5271101SLydia Wang analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); 192f5271101SLydia Wang return change; 193f5271101SLydia Wang } 194f5271101SLydia Wang 195f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 196f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 197f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 198f5271101SLydia Wang .name = NULL, \ 199f5271101SLydia Wang .index = 0, \ 200f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 201f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 202f5271101SLydia Wang .put = analog_input_switch_put, \ 203f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 204f5271101SLydia Wang 205c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_control_templates[] = { 206c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 207c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 208f5271101SLydia Wang ANALOG_INPUT_MUTE, 209c577b8a1SJoseph Chan }; 210c577b8a1SJoseph Chan 211c577b8a1SJoseph Chan 212c577b8a1SJoseph Chan struct via_spec { 213c577b8a1SJoseph Chan /* codec parameterization */ 214c577b8a1SJoseph Chan struct snd_kcontrol_new *mixers[3]; 215c577b8a1SJoseph Chan unsigned int num_mixers; 216c577b8a1SJoseph Chan 21769e52a80SHarald Welte struct hda_verb *init_verbs[5]; 21869e52a80SHarald Welte unsigned int num_iverbs; 219c577b8a1SJoseph Chan 220c577b8a1SJoseph Chan char *stream_name_analog; 221c577b8a1SJoseph Chan struct hda_pcm_stream *stream_analog_playback; 222c577b8a1SJoseph Chan struct hda_pcm_stream *stream_analog_capture; 223c577b8a1SJoseph Chan 224c577b8a1SJoseph Chan char *stream_name_digital; 225c577b8a1SJoseph Chan struct hda_pcm_stream *stream_digital_playback; 226c577b8a1SJoseph Chan struct hda_pcm_stream *stream_digital_capture; 227c577b8a1SJoseph Chan 228c577b8a1SJoseph Chan /* playback */ 229c577b8a1SJoseph Chan struct hda_multi_out multiout; 2309da29271STakashi Iwai hda_nid_t slave_dig_outs[2]; 231c577b8a1SJoseph Chan 232c577b8a1SJoseph Chan /* capture */ 233c577b8a1SJoseph Chan unsigned int num_adc_nids; 234c577b8a1SJoseph Chan hda_nid_t *adc_nids; 235337b9d02STakashi Iwai hda_nid_t mux_nids[3]; 236c577b8a1SJoseph Chan hda_nid_t dig_in_nid; 23755d1d6c1STakashi Iwai hda_nid_t dig_in_pin; 238c577b8a1SJoseph Chan 239c577b8a1SJoseph Chan /* capture source */ 240c577b8a1SJoseph Chan const struct hda_input_mux *input_mux; 241c577b8a1SJoseph Chan unsigned int cur_mux[3]; 242c577b8a1SJoseph Chan 243c577b8a1SJoseph Chan /* PCM information */ 24498aa34c0SHarald Welte struct hda_pcm pcm_rec[3]; 245c577b8a1SJoseph Chan 246c577b8a1SJoseph Chan /* dynamic controls, init_verbs and input_mux */ 247c577b8a1SJoseph Chan struct auto_pin_cfg autocfg; 248603c4019STakashi Iwai struct snd_array kctls; 2490aa62aefSHarald Welte struct hda_input_mux private_imux[2]; 25041923e44STakashi Iwai hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 251cb53c626STakashi Iwai 2520aa62aefSHarald Welte /* HP mode source */ 2530aa62aefSHarald Welte const struct hda_input_mux *hp_mux; 2540aa62aefSHarald Welte unsigned int hp_independent_mode; 2550aa62aefSHarald Welte 256518bf3baSLydia Wang enum VIA_HDA_CODEC codec_type; 257518bf3baSLydia Wang 258cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 259cb53c626STakashi Iwai struct hda_loopback_check loopback; 260cb53c626STakashi Iwai #endif 261c577b8a1SJoseph Chan }; 262c577b8a1SJoseph Chan 263c577b8a1SJoseph Chan static hda_nid_t vt1708_adc_nids[2] = { 264c577b8a1SJoseph Chan /* ADC1-2 */ 265c577b8a1SJoseph Chan 0x15, 0x27 266c577b8a1SJoseph Chan }; 267c577b8a1SJoseph Chan 268c577b8a1SJoseph Chan static hda_nid_t vt1709_adc_nids[3] = { 269c577b8a1SJoseph Chan /* ADC1-2 */ 270c577b8a1SJoseph Chan 0x14, 0x15, 0x16 271c577b8a1SJoseph Chan }; 272c577b8a1SJoseph Chan 273f7278fd0SJosepch Chan static hda_nid_t vt1708B_adc_nids[2] = { 274f7278fd0SJosepch Chan /* ADC1-2 */ 275f7278fd0SJosepch Chan 0x13, 0x14 276f7278fd0SJosepch Chan }; 277f7278fd0SJosepch Chan 278d949cac1SHarald Welte static hda_nid_t vt1708S_adc_nids[2] = { 279d949cac1SHarald Welte /* ADC1-2 */ 280d949cac1SHarald Welte 0x13, 0x14 281d949cac1SHarald Welte }; 282d949cac1SHarald Welte 283d949cac1SHarald Welte static hda_nid_t vt1702_adc_nids[3] = { 284d949cac1SHarald Welte /* ADC1-2 */ 285d949cac1SHarald Welte 0x12, 0x20, 0x1F 286d949cac1SHarald Welte }; 287d949cac1SHarald Welte 288c577b8a1SJoseph Chan /* add dynamic controls */ 289c577b8a1SJoseph Chan static int via_add_control(struct via_spec *spec, int type, const char *name, 290c577b8a1SJoseph Chan unsigned long val) 291c577b8a1SJoseph Chan { 292c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 293c577b8a1SJoseph Chan 294603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 295603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 296c577b8a1SJoseph Chan if (!knew) 297c577b8a1SJoseph Chan return -ENOMEM; 298c577b8a1SJoseph Chan *knew = vt1708_control_templates[type]; 299c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 300c577b8a1SJoseph Chan if (!knew->name) 301c577b8a1SJoseph Chan return -ENOMEM; 302c577b8a1SJoseph Chan knew->private_value = val; 303c577b8a1SJoseph Chan return 0; 304c577b8a1SJoseph Chan } 305c577b8a1SJoseph Chan 306603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 307603c4019STakashi Iwai { 308603c4019STakashi Iwai struct via_spec *spec = codec->spec; 309603c4019STakashi Iwai 310603c4019STakashi Iwai if (spec->kctls.list) { 311603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 312603c4019STakashi Iwai int i; 313603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 314603c4019STakashi Iwai kfree(kctl[i].name); 315603c4019STakashi Iwai } 316603c4019STakashi Iwai snd_array_free(&spec->kctls); 317603c4019STakashi Iwai } 318603c4019STakashi Iwai 319c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 3209510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 3219510e8ddSLydia Wang int idx, int mix_nid) 322c577b8a1SJoseph Chan { 323c577b8a1SJoseph Chan char name[32]; 324c577b8a1SJoseph Chan int err; 325c577b8a1SJoseph Chan 326c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 327c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 328c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 329c577b8a1SJoseph Chan if (err < 0) 330c577b8a1SJoseph Chan return err; 331c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 332f5271101SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, 333c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 334c577b8a1SJoseph Chan if (err < 0) 335c577b8a1SJoseph Chan return err; 336c577b8a1SJoseph Chan return 0; 337c577b8a1SJoseph Chan } 338c577b8a1SJoseph Chan 339c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec, 340c577b8a1SJoseph Chan hda_nid_t nid, int pin_type, 341c577b8a1SJoseph Chan int dac_idx) 342c577b8a1SJoseph Chan { 343c577b8a1SJoseph Chan /* set as output */ 344c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 345c577b8a1SJoseph Chan pin_type); 346c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 347c577b8a1SJoseph Chan AMP_OUT_UNMUTE); 348d3a11e60STakashi Iwai if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) 349d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 350d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 351c577b8a1SJoseph Chan } 352c577b8a1SJoseph Chan 353c577b8a1SJoseph Chan 354c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 355c577b8a1SJoseph Chan { 356c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 357c577b8a1SJoseph Chan int i; 358c577b8a1SJoseph Chan 359c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 360c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.line_out_pins[i]; 361c577b8a1SJoseph Chan if (nid) 362c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); 363c577b8a1SJoseph Chan } 364c577b8a1SJoseph Chan } 365c577b8a1SJoseph Chan 366c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 367c577b8a1SJoseph Chan { 368c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 369c577b8a1SJoseph Chan hda_nid_t pin; 370c577b8a1SJoseph Chan 371c577b8a1SJoseph Chan pin = spec->autocfg.hp_pins[0]; 372c577b8a1SJoseph Chan if (pin) /* connect to front */ 373c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); 374c577b8a1SJoseph Chan } 375c577b8a1SJoseph Chan 376c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 377c577b8a1SJoseph Chan { 378c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 379c577b8a1SJoseph Chan int i; 380c577b8a1SJoseph Chan 381c577b8a1SJoseph Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 382c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.input_pins[i]; 383c577b8a1SJoseph Chan 384c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 385c577b8a1SJoseph Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 386c577b8a1SJoseph Chan (i <= AUTO_PIN_FRONT_MIC ? 387c577b8a1SJoseph Chan PIN_VREF50 : PIN_IN)); 388c577b8a1SJoseph Chan 389c577b8a1SJoseph Chan } 390c577b8a1SJoseph Chan } 391f5271101SLydia Wang 392f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 393f5271101SLydia Wang unsigned int *affected_parm) 394f5271101SLydia Wang { 395f5271101SLydia Wang unsigned parm; 396f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 397f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 398f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 399f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 400f5271101SLydia Wang unsigned present = snd_hda_codec_read(codec, nid, 0, 401f5271101SLydia Wang AC_VERB_GET_PIN_SENSE, 0) >> 31; 402f5271101SLydia Wang 403f5271101SLydia Wang if ((no_presence || present) && get_defcfg_connect(def_conf) 404f5271101SLydia Wang != AC_JACK_PORT_NONE) { 405f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 406f5271101SLydia Wang parm = AC_PWRST_D0; 407f5271101SLydia Wang } else 408f5271101SLydia Wang parm = AC_PWRST_D3; 409f5271101SLydia Wang 410f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 411f5271101SLydia Wang } 412f5271101SLydia Wang 413f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec) 414f5271101SLydia Wang { 415f5271101SLydia Wang struct via_spec *spec = codec->spec; 416f5271101SLydia Wang int imux_is_smixer; 417f5271101SLydia Wang unsigned int parm; 418f5271101SLydia Wang 419f5271101SLydia Wang if (spec->codec_type == VT1702) { 420f5271101SLydia Wang imux_is_smixer = snd_hda_codec_read( 421f5271101SLydia Wang codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 422f5271101SLydia Wang /* inputs */ 423f5271101SLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 424f5271101SLydia Wang parm = AC_PWRST_D3; 425f5271101SLydia Wang set_pin_power_state(codec, 0x14, &parm); 426f5271101SLydia Wang set_pin_power_state(codec, 0x15, &parm); 427f5271101SLydia Wang set_pin_power_state(codec, 0x18, &parm); 428f5271101SLydia Wang if (imux_is_smixer) 429f5271101SLydia Wang parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */ 430f5271101SLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 431f5271101SLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, 432f5271101SLydia Wang parm); 433f5271101SLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, 434f5271101SLydia Wang parm); 435f5271101SLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, 436f5271101SLydia Wang parm); 437f5271101SLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, 438f5271101SLydia Wang parm); 439f5271101SLydia Wang 440f5271101SLydia Wang /* outputs */ 441f5271101SLydia Wang /* PW 3/4 (16h/17h) */ 442f5271101SLydia Wang parm = AC_PWRST_D3; 443f5271101SLydia Wang set_pin_power_state(codec, 0x16, &parm); 444f5271101SLydia Wang set_pin_power_state(codec, 0x17, &parm); 445f5271101SLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 446f5271101SLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 447f5271101SLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 448f5271101SLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 449f5271101SLydia Wang parm); 450f5271101SLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, 451f5271101SLydia Wang parm); 452f5271101SLydia Wang } else if (spec->codec_type == VT1708B_8CH 453f5271101SLydia Wang || spec->codec_type == VT1708B_4CH 454f5271101SLydia Wang || spec->codec_type == VT1708S) { 455f5271101SLydia Wang /* SW0 (17h) = stereo mixer */ 456f5271101SLydia Wang int is_8ch = spec->codec_type != VT1708B_4CH; 457f5271101SLydia Wang imux_is_smixer = snd_hda_codec_read( 458f5271101SLydia Wang codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 459f5271101SLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0); 460f5271101SLydia Wang /* inputs */ 461f5271101SLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 462f5271101SLydia Wang parm = AC_PWRST_D3; 463f5271101SLydia Wang set_pin_power_state(codec, 0x1a, &parm); 464f5271101SLydia Wang set_pin_power_state(codec, 0x1b, &parm); 465f5271101SLydia Wang set_pin_power_state(codec, 0x1e, &parm); 466f5271101SLydia Wang if (imux_is_smixer) 467f5271101SLydia Wang parm = AC_PWRST_D0; 468f5271101SLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 469f5271101SLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, 470f5271101SLydia Wang parm); 471f5271101SLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, 472f5271101SLydia Wang parm); 473f5271101SLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, 474f5271101SLydia Wang parm); 475f5271101SLydia Wang 476f5271101SLydia Wang /* outputs */ 477f5271101SLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 478f5271101SLydia Wang parm = AC_PWRST_D3; 479f5271101SLydia Wang set_pin_power_state(codec, 0x19, &parm); 480f5271101SLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, 481f5271101SLydia Wang parm); 482f5271101SLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, 483f5271101SLydia Wang parm); 484f5271101SLydia Wang 485f5271101SLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 486f5271101SLydia Wang if (is_8ch) { 487f5271101SLydia Wang parm = AC_PWRST_D3; 488f5271101SLydia Wang set_pin_power_state(codec, 0x22, &parm); 489f5271101SLydia Wang snd_hda_codec_write(codec, 0x26, 0, 490f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 491f5271101SLydia Wang snd_hda_codec_write(codec, 0x24, 0, 492f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 493f5271101SLydia Wang } 494f5271101SLydia Wang 495f5271101SLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 496f5271101SLydia Wang parm = AC_PWRST_D3; 497f5271101SLydia Wang /* force to D0 for internal Speaker */ 498f5271101SLydia Wang set_pin_power_state(codec, 0x1c, &parm); 499f5271101SLydia Wang set_pin_power_state(codec, 0x1d, &parm); 500f5271101SLydia Wang if (is_8ch) 501f5271101SLydia Wang set_pin_power_state(codec, 0x23, &parm); 502f5271101SLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 503f5271101SLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 504f5271101SLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 505f5271101SLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 506f5271101SLydia Wang parm); 507f5271101SLydia Wang if (is_8ch) { 508f5271101SLydia Wang snd_hda_codec_write(codec, 0x25, 0, 509f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 510f5271101SLydia Wang snd_hda_codec_write(codec, 0x27, 0, 511f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 512f5271101SLydia Wang } 513f5271101SLydia Wang } 514f5271101SLydia Wang } 515f5271101SLydia Wang 516c577b8a1SJoseph Chan /* 517c577b8a1SJoseph Chan * input MUX handling 518c577b8a1SJoseph Chan */ 519c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 520c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 521c577b8a1SJoseph Chan { 522c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 523c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 524c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 525c577b8a1SJoseph Chan } 526c577b8a1SJoseph Chan 527c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 528c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 529c577b8a1SJoseph Chan { 530c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 531c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 532c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 533c577b8a1SJoseph Chan 534c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 535c577b8a1SJoseph Chan return 0; 536c577b8a1SJoseph Chan } 537c577b8a1SJoseph Chan 538c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 539c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 540c577b8a1SJoseph Chan { 541c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 542c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 543c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 544c577b8a1SJoseph Chan 545337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 546337b9d02STakashi Iwai return -EINVAL; 547c577b8a1SJoseph Chan return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 548337b9d02STakashi Iwai spec->mux_nids[adc_idx], 549c577b8a1SJoseph Chan &spec->cur_mux[adc_idx]); 550c577b8a1SJoseph Chan } 551c577b8a1SJoseph Chan 5520aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 5530aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 5540aa62aefSHarald Welte { 5550aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 5560aa62aefSHarald Welte struct via_spec *spec = codec->spec; 5570aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 5580aa62aefSHarald Welte } 5590aa62aefSHarald Welte 5600aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 5610aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 5620aa62aefSHarald Welte { 5630aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 5640aa62aefSHarald Welte struct via_spec *spec = codec->spec; 5650aa62aefSHarald Welte hda_nid_t nid = spec->autocfg.hp_pins[0]; 5660aa62aefSHarald Welte unsigned int pinsel = snd_hda_codec_read(codec, nid, 0, 5670aa62aefSHarald Welte AC_VERB_GET_CONNECT_SEL, 5680aa62aefSHarald Welte 0x00); 5690aa62aefSHarald Welte 5700aa62aefSHarald Welte ucontrol->value.enumerated.item[0] = pinsel; 5710aa62aefSHarald Welte 5720aa62aefSHarald Welte return 0; 5730aa62aefSHarald Welte } 5740aa62aefSHarald Welte 575*0713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active) 576*0713efebSLydia Wang { 577*0713efebSLydia Wang struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); 578*0713efebSLydia Wang if (ctl) { 579*0713efebSLydia Wang ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 580*0713efebSLydia Wang ctl->vd[0].access |= active 581*0713efebSLydia Wang ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; 582*0713efebSLydia Wang snd_ctl_notify(codec->bus->card, 583*0713efebSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); 584*0713efebSLydia Wang } 585*0713efebSLydia Wang } 586*0713efebSLydia Wang 5870aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 5880aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 5890aa62aefSHarald Welte { 5900aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 5910aa62aefSHarald Welte struct via_spec *spec = codec->spec; 5920aa62aefSHarald Welte hda_nid_t nid = spec->autocfg.hp_pins[0]; 5930aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 5940aa62aefSHarald Welte unsigned int con_nid = snd_hda_codec_read(codec, nid, 0, 5950aa62aefSHarald Welte AC_VERB_GET_CONNECT_LIST, 0) & 0xff; 5960aa62aefSHarald Welte 5970aa62aefSHarald Welte if (con_nid == spec->multiout.hp_nid) { 5980aa62aefSHarald Welte if (pinsel == 0) { 5990aa62aefSHarald Welte if (!spec->hp_independent_mode) { 6000aa62aefSHarald Welte if (spec->multiout.num_dacs > 1) 6010aa62aefSHarald Welte spec->multiout.num_dacs -= 1; 6020aa62aefSHarald Welte spec->hp_independent_mode = 1; 6030aa62aefSHarald Welte } 6040aa62aefSHarald Welte } else if (pinsel == 1) { 6050aa62aefSHarald Welte if (spec->hp_independent_mode) { 6060aa62aefSHarald Welte if (spec->multiout.num_dacs > 1) 6070aa62aefSHarald Welte spec->multiout.num_dacs += 1; 6080aa62aefSHarald Welte spec->hp_independent_mode = 0; 6090aa62aefSHarald Welte } 6100aa62aefSHarald Welte } 6110aa62aefSHarald Welte } else { 6120aa62aefSHarald Welte if (pinsel == 0) { 6130aa62aefSHarald Welte if (spec->hp_independent_mode) { 6140aa62aefSHarald Welte if (spec->multiout.num_dacs > 1) 6150aa62aefSHarald Welte spec->multiout.num_dacs += 1; 6160aa62aefSHarald Welte spec->hp_independent_mode = 0; 6170aa62aefSHarald Welte } 6180aa62aefSHarald Welte } else if (pinsel == 1) { 6190aa62aefSHarald Welte if (!spec->hp_independent_mode) { 6200aa62aefSHarald Welte if (spec->multiout.num_dacs > 1) 6210aa62aefSHarald Welte spec->multiout.num_dacs -= 1; 6220aa62aefSHarald Welte spec->hp_independent_mode = 1; 6230aa62aefSHarald Welte } 6240aa62aefSHarald Welte } 6250aa62aefSHarald Welte } 6260aa62aefSHarald Welte snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, 6270aa62aefSHarald Welte pinsel); 6280aa62aefSHarald Welte 6290aa62aefSHarald Welte if (spec->multiout.hp_nid && 6300aa62aefSHarald Welte spec->multiout.hp_nid != spec->multiout.dac_nids[HDA_FRONT]) 6310aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 6320aa62aefSHarald Welte spec->multiout.hp_nid, 6330aa62aefSHarald Welte 0, 0, 0); 6340aa62aefSHarald Welte 635*0713efebSLydia Wang /* update HP volume/swtich active state */ 636*0713efebSLydia Wang if (spec->codec_type == VT1708S 637*0713efebSLydia Wang || spec->codec_type == VT1702) { 638*0713efebSLydia Wang activate_ctl(codec, "Headphone Playback Volume", 639*0713efebSLydia Wang spec->hp_independent_mode); 640*0713efebSLydia Wang activate_ctl(codec, "Headphone Playback Switch", 641*0713efebSLydia Wang spec->hp_independent_mode); 642*0713efebSLydia Wang } 6430aa62aefSHarald Welte return 0; 6440aa62aefSHarald Welte } 6450aa62aefSHarald Welte 6460aa62aefSHarald Welte static struct snd_kcontrol_new via_hp_mixer[] = { 6470aa62aefSHarald Welte { 6480aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 6490aa62aefSHarald Welte .name = "Independent HP", 6500aa62aefSHarald Welte .count = 1, 6510aa62aefSHarald Welte .info = via_independent_hp_info, 6520aa62aefSHarald Welte .get = via_independent_hp_get, 6530aa62aefSHarald Welte .put = via_independent_hp_put, 6540aa62aefSHarald Welte }, 6550aa62aefSHarald Welte { } /* end */ 6560aa62aefSHarald Welte }; 6570aa62aefSHarald Welte 658c577b8a1SJoseph Chan /* capture mixer elements */ 659c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_capture_mixer[] = { 660c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), 661c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), 662c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), 663c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), 664c577b8a1SJoseph Chan { 665c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 666c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 667c577b8a1SJoseph Chan * So call somewhat different.. 668c577b8a1SJoseph Chan */ 669c577b8a1SJoseph Chan /* .name = "Capture Source", */ 670c577b8a1SJoseph Chan .name = "Input Source", 671c577b8a1SJoseph Chan .count = 1, 672c577b8a1SJoseph Chan .info = via_mux_enum_info, 673c577b8a1SJoseph Chan .get = via_mux_enum_get, 674c577b8a1SJoseph Chan .put = via_mux_enum_put, 675c577b8a1SJoseph Chan }, 676c577b8a1SJoseph Chan { } /* end */ 677c577b8a1SJoseph Chan }; 678f5271101SLydia Wang 679f5271101SLydia Wang /* check AA path's mute statue */ 680f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec) 681f5271101SLydia Wang { 682f5271101SLydia Wang int mute = 1; 683f5271101SLydia Wang hda_nid_t nid_mixer; 684f5271101SLydia Wang int start_idx; 685f5271101SLydia Wang int end_idx; 686f5271101SLydia Wang int i; 687f5271101SLydia Wang struct via_spec *spec = codec->spec; 688f5271101SLydia Wang /* get nid of MW0 and start & end index */ 689f5271101SLydia Wang switch (spec->codec_type) { 690f5271101SLydia Wang case VT1708B_8CH: 691f5271101SLydia Wang case VT1708B_4CH: 692f5271101SLydia Wang case VT1708S: 693f5271101SLydia Wang nid_mixer = 0x16; 694f5271101SLydia Wang start_idx = 2; 695f5271101SLydia Wang end_idx = 4; 696f5271101SLydia Wang break; 697f5271101SLydia Wang case VT1702: 698f5271101SLydia Wang nid_mixer = 0x1a; 699f5271101SLydia Wang start_idx = 1; 700f5271101SLydia Wang end_idx = 3; 701f5271101SLydia Wang break; 702f5271101SLydia Wang default: 703f5271101SLydia Wang return 0; 704f5271101SLydia Wang } 705f5271101SLydia Wang /* check AA path's mute status */ 706f5271101SLydia Wang for (i = start_idx; i <= end_idx; i++) { 707f5271101SLydia Wang unsigned int con_list = snd_hda_codec_read( 708f5271101SLydia Wang codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); 709f5271101SLydia Wang int shift = 8 * (i % 4); 710f5271101SLydia Wang hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; 711f5271101SLydia Wang unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); 712f5271101SLydia Wang if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { 713f5271101SLydia Wang /* check mute status while the pin is connected */ 714f5271101SLydia Wang int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, 715f5271101SLydia Wang HDA_INPUT, i) >> 7; 716f5271101SLydia Wang int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, 717f5271101SLydia Wang HDA_INPUT, i) >> 7; 718f5271101SLydia Wang if (!mute_l || !mute_r) { 719f5271101SLydia Wang mute = 0; 720f5271101SLydia Wang break; 721f5271101SLydia Wang } 722f5271101SLydia Wang } 723f5271101SLydia Wang } 724f5271101SLydia Wang return mute; 725f5271101SLydia Wang } 726f5271101SLydia Wang 727f5271101SLydia Wang /* enter/exit analog low-current mode */ 728f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) 729f5271101SLydia Wang { 730f5271101SLydia Wang struct via_spec *spec = codec->spec; 731f5271101SLydia Wang static int saved_stream_idle = 1; /* saved stream idle status */ 732f5271101SLydia Wang int enable = is_aa_path_mute(codec); 733f5271101SLydia Wang unsigned int verb = 0; 734f5271101SLydia Wang unsigned int parm = 0; 735f5271101SLydia Wang 736f5271101SLydia Wang if (stream_idle == -1) /* stream status did not change */ 737f5271101SLydia Wang enable = enable && saved_stream_idle; 738f5271101SLydia Wang else { 739f5271101SLydia Wang enable = enable && stream_idle; 740f5271101SLydia Wang saved_stream_idle = stream_idle; 741f5271101SLydia Wang } 742f5271101SLydia Wang 743f5271101SLydia Wang /* decide low current mode's verb & parameter */ 744f5271101SLydia Wang switch (spec->codec_type) { 745f5271101SLydia Wang case VT1708B_8CH: 746f5271101SLydia Wang case VT1708B_4CH: 747f5271101SLydia Wang verb = 0xf70; 748f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 749f5271101SLydia Wang break; 750f5271101SLydia Wang case VT1708S: 751f5271101SLydia Wang verb = 0xf73; 752f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 753f5271101SLydia Wang break; 754f5271101SLydia Wang case VT1702: 755f5271101SLydia Wang verb = 0xf73; 756f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 757f5271101SLydia Wang break; 758f5271101SLydia Wang default: 759f5271101SLydia Wang return; /* other codecs are not supported */ 760f5271101SLydia Wang } 761f5271101SLydia Wang /* send verb */ 762f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 763f5271101SLydia Wang } 764f5271101SLydia Wang 765c577b8a1SJoseph Chan /* 766c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 767c577b8a1SJoseph Chan */ 768c577b8a1SJoseph Chan static struct hda_verb vt1708_volume_init_verbs[] = { 769c577b8a1SJoseph Chan /* 770c577b8a1SJoseph Chan * Unmute ADC0-1 and set the default input to mic-in 771c577b8a1SJoseph Chan */ 772c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 773c577b8a1SJoseph Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 774c577b8a1SJoseph Chan 775c577b8a1SJoseph Chan 776f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 777c577b8a1SJoseph Chan * mixer widget 778c577b8a1SJoseph Chan */ 779c577b8a1SJoseph Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 780f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 781f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 782f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 783f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 784f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 785c577b8a1SJoseph Chan 786c577b8a1SJoseph Chan /* 787c577b8a1SJoseph Chan * Set up output mixers (0x19 - 0x1b) 788c577b8a1SJoseph Chan */ 789c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 790c577b8a1SJoseph Chan {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 791c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 792c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 793c577b8a1SJoseph Chan 794c577b8a1SJoseph Chan /* Setup default input to PW4 */ 795c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, 796c577b8a1SJoseph Chan /* PW9 Output enable */ 797c577b8a1SJoseph Chan {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 798f7278fd0SJosepch Chan { } 799c577b8a1SJoseph Chan }; 800c577b8a1SJoseph Chan 801c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, 802c577b8a1SJoseph Chan struct hda_codec *codec, 803c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 804c577b8a1SJoseph Chan { 805c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 80617314379SLydia Wang int idle = substream->pstr->substream_opened == 1 80717314379SLydia Wang && substream->ref_count == 0; 80817314379SLydia Wang 80917314379SLydia Wang analog_low_current_mode(codec, idle); 8109a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 8119a08160bSTakashi Iwai hinfo); 812c577b8a1SJoseph Chan } 813c577b8a1SJoseph Chan 814c577b8a1SJoseph Chan static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 815c577b8a1SJoseph Chan struct hda_codec *codec, 816c577b8a1SJoseph Chan unsigned int stream_tag, 817c577b8a1SJoseph Chan unsigned int format, 818c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 819c577b8a1SJoseph Chan { 820c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 821c577b8a1SJoseph Chan return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, 822c577b8a1SJoseph Chan stream_tag, format, substream); 823c577b8a1SJoseph Chan } 824c577b8a1SJoseph Chan 825c577b8a1SJoseph Chan static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 826c577b8a1SJoseph Chan struct hda_codec *codec, 827c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 828c577b8a1SJoseph Chan { 829c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 830c577b8a1SJoseph Chan return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); 831c577b8a1SJoseph Chan } 832c577b8a1SJoseph Chan 8330aa62aefSHarald Welte 8340aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec, 8350aa62aefSHarald Welte unsigned int stream_tag, 8360aa62aefSHarald Welte unsigned int format, 8370aa62aefSHarald Welte struct snd_pcm_substream *substream) 8380aa62aefSHarald Welte { 8390aa62aefSHarald Welte struct via_spec *spec = codec->spec; 8400aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 8410aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 8420aa62aefSHarald Welte int chs = substream->runtime->channels; 8430aa62aefSHarald Welte int i; 8440aa62aefSHarald Welte 8450aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 8460aa62aefSHarald Welte if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { 8470aa62aefSHarald Welte if (chs == 2 && 8480aa62aefSHarald Welte snd_hda_is_supported_format(codec, mout->dig_out_nid, 8490aa62aefSHarald Welte format) && 8500aa62aefSHarald Welte !(codec->spdif_status & IEC958_AES0_NONAUDIO)) { 8510aa62aefSHarald Welte mout->dig_out_used = HDA_DIG_ANALOG_DUP; 8520aa62aefSHarald Welte /* turn off SPDIF once; otherwise the IEC958 bits won't 8530aa62aefSHarald Welte * be updated */ 8540aa62aefSHarald Welte if (codec->spdif_ctls & AC_DIG1_ENABLE) 8550aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 8560aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 8570aa62aefSHarald Welte codec->spdif_ctls & 8580aa62aefSHarald Welte ~AC_DIG1_ENABLE & 0xff); 8590aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 8600aa62aefSHarald Welte stream_tag, 0, format); 8610aa62aefSHarald Welte /* turn on again (if needed) */ 8620aa62aefSHarald Welte if (codec->spdif_ctls & AC_DIG1_ENABLE) 8630aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 8640aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 8650aa62aefSHarald Welte codec->spdif_ctls & 0xff); 8660aa62aefSHarald Welte } else { 8670aa62aefSHarald Welte mout->dig_out_used = 0; 8680aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 8690aa62aefSHarald Welte 0, 0, 0); 8700aa62aefSHarald Welte } 8710aa62aefSHarald Welte } 8720aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 8730aa62aefSHarald Welte 8740aa62aefSHarald Welte /* front */ 8750aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 8760aa62aefSHarald Welte 0, format); 8770aa62aefSHarald Welte 8780aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 8790aa62aefSHarald Welte !spec->hp_independent_mode) 8800aa62aefSHarald Welte /* headphone out will just decode front left/right (stereo) */ 8810aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 8820aa62aefSHarald Welte 0, format); 8830aa62aefSHarald Welte 8840aa62aefSHarald Welte /* extra outputs copied from front */ 8850aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 8860aa62aefSHarald Welte if (mout->extra_out_nid[i]) 8870aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 8880aa62aefSHarald Welte mout->extra_out_nid[i], 8890aa62aefSHarald Welte stream_tag, 0, format); 8900aa62aefSHarald Welte 8910aa62aefSHarald Welte /* surrounds */ 8920aa62aefSHarald Welte for (i = 1; i < mout->num_dacs; i++) { 8930aa62aefSHarald Welte if (chs >= (i + 1) * 2) /* independent out */ 8940aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 8950aa62aefSHarald Welte i * 2, format); 8960aa62aefSHarald Welte else /* copy front */ 8970aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 8980aa62aefSHarald Welte 0, format); 8990aa62aefSHarald Welte } 9000aa62aefSHarald Welte } 9010aa62aefSHarald Welte 9020aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 9030aa62aefSHarald Welte struct hda_codec *codec, 9040aa62aefSHarald Welte unsigned int stream_tag, 9050aa62aefSHarald Welte unsigned int format, 9060aa62aefSHarald Welte struct snd_pcm_substream *substream) 9070aa62aefSHarald Welte { 9080aa62aefSHarald Welte struct via_spec *spec = codec->spec; 9090aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 9100aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 9110aa62aefSHarald Welte 9120aa62aefSHarald Welte if (substream->number == 0) 9130aa62aefSHarald Welte playback_multi_pcm_prep_0(codec, stream_tag, format, 9140aa62aefSHarald Welte substream); 9150aa62aefSHarald Welte else { 9160aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 9170aa62aefSHarald Welte spec->hp_independent_mode) 9180aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 9190aa62aefSHarald Welte stream_tag, 0, format); 9200aa62aefSHarald Welte } 9210aa62aefSHarald Welte 9220aa62aefSHarald Welte return 0; 9230aa62aefSHarald Welte } 9240aa62aefSHarald Welte 9250aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 9260aa62aefSHarald Welte struct hda_codec *codec, 9270aa62aefSHarald Welte struct snd_pcm_substream *substream) 9280aa62aefSHarald Welte { 9290aa62aefSHarald Welte struct via_spec *spec = codec->spec; 9300aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 9310aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 9320aa62aefSHarald Welte int i; 9330aa62aefSHarald Welte 9340aa62aefSHarald Welte if (substream->number == 0) { 9350aa62aefSHarald Welte for (i = 0; i < mout->num_dacs; i++) 9360aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); 9370aa62aefSHarald Welte 9380aa62aefSHarald Welte if (mout->hp_nid && !spec->hp_independent_mode) 9390aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 9400aa62aefSHarald Welte 0, 0, 0); 9410aa62aefSHarald Welte 9420aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 9430aa62aefSHarald Welte if (mout->extra_out_nid[i]) 9440aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 9450aa62aefSHarald Welte mout->extra_out_nid[i], 9460aa62aefSHarald Welte 0, 0, 0); 9470aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 9480aa62aefSHarald Welte if (mout->dig_out_nid && 9490aa62aefSHarald Welte mout->dig_out_used == HDA_DIG_ANALOG_DUP) { 9500aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 9510aa62aefSHarald Welte 0, 0, 0); 9520aa62aefSHarald Welte mout->dig_out_used = 0; 9530aa62aefSHarald Welte } 9540aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 9550aa62aefSHarald Welte } else { 9560aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 9570aa62aefSHarald Welte spec->hp_independent_mode) 9580aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 9590aa62aefSHarald Welte 0, 0, 0); 9600aa62aefSHarald Welte } 9610aa62aefSHarald Welte 9620aa62aefSHarald Welte return 0; 9630aa62aefSHarald Welte } 9640aa62aefSHarald Welte 965c577b8a1SJoseph Chan /* 966c577b8a1SJoseph Chan * Digital out 967c577b8a1SJoseph Chan */ 968c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 969c577b8a1SJoseph Chan struct hda_codec *codec, 970c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 971c577b8a1SJoseph Chan { 972c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 973c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 974c577b8a1SJoseph Chan } 975c577b8a1SJoseph Chan 976c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 977c577b8a1SJoseph Chan struct hda_codec *codec, 978c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 979c577b8a1SJoseph Chan { 980c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 981c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 982c577b8a1SJoseph Chan } 983c577b8a1SJoseph Chan 9845691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 98598aa34c0SHarald Welte struct hda_codec *codec, 98698aa34c0SHarald Welte unsigned int stream_tag, 98798aa34c0SHarald Welte unsigned int format, 98898aa34c0SHarald Welte struct snd_pcm_substream *substream) 98998aa34c0SHarald Welte { 99098aa34c0SHarald Welte struct via_spec *spec = codec->spec; 9919da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 9929da29271STakashi Iwai stream_tag, format, substream); 9939da29271STakashi Iwai } 9945691ec7fSHarald Welte 9959da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 9969da29271STakashi Iwai struct hda_codec *codec, 9979da29271STakashi Iwai struct snd_pcm_substream *substream) 9989da29271STakashi Iwai { 9999da29271STakashi Iwai struct via_spec *spec = codec->spec; 10009da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 100198aa34c0SHarald Welte return 0; 100298aa34c0SHarald Welte } 100398aa34c0SHarald Welte 1004c577b8a1SJoseph Chan /* 1005c577b8a1SJoseph Chan * Analog capture 1006c577b8a1SJoseph Chan */ 1007c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1008c577b8a1SJoseph Chan struct hda_codec *codec, 1009c577b8a1SJoseph Chan unsigned int stream_tag, 1010c577b8a1SJoseph Chan unsigned int format, 1011c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1012c577b8a1SJoseph Chan { 1013c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1014c577b8a1SJoseph Chan 1015c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1016c577b8a1SJoseph Chan stream_tag, 0, format); 1017c577b8a1SJoseph Chan return 0; 1018c577b8a1SJoseph Chan } 1019c577b8a1SJoseph Chan 1020c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1021c577b8a1SJoseph Chan struct hda_codec *codec, 1022c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1023c577b8a1SJoseph Chan { 1024c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1025888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1026c577b8a1SJoseph Chan return 0; 1027c577b8a1SJoseph Chan } 1028c577b8a1SJoseph Chan 1029c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_playback = { 10300aa62aefSHarald Welte .substreams = 2, 1031c577b8a1SJoseph Chan .channels_min = 2, 1032c577b8a1SJoseph Chan .channels_max = 8, 1033c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 1034c577b8a1SJoseph Chan .ops = { 1035c577b8a1SJoseph Chan .open = via_playback_pcm_open, 10360aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 10370aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1038c577b8a1SJoseph Chan }, 1039c577b8a1SJoseph Chan }; 1040c577b8a1SJoseph Chan 1041bc9b5623STakashi Iwai static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 1042bc9b5623STakashi Iwai .substreams = 1, 1043bc9b5623STakashi Iwai .channels_min = 2, 1044bc9b5623STakashi Iwai .channels_max = 8, 1045bc9b5623STakashi Iwai .nid = 0x10, /* NID to query formats and rates */ 1046bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1047bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1048bc9b5623STakashi Iwai * disable the 24bit format, so far. 1049bc9b5623STakashi Iwai */ 1050bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1051bc9b5623STakashi Iwai .ops = { 1052bc9b5623STakashi Iwai .open = via_playback_pcm_open, 1053bc9b5623STakashi Iwai .prepare = via_playback_pcm_prepare, 1054bc9b5623STakashi Iwai .cleanup = via_playback_pcm_cleanup 1055bc9b5623STakashi Iwai }, 1056bc9b5623STakashi Iwai }; 1057bc9b5623STakashi Iwai 1058c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_capture = { 1059c577b8a1SJoseph Chan .substreams = 2, 1060c577b8a1SJoseph Chan .channels_min = 2, 1061c577b8a1SJoseph Chan .channels_max = 2, 1062c577b8a1SJoseph Chan .nid = 0x15, /* NID to query formats and rates */ 1063c577b8a1SJoseph Chan .ops = { 1064c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1065c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1066c577b8a1SJoseph Chan }, 1067c577b8a1SJoseph Chan }; 1068c577b8a1SJoseph Chan 1069c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_playback = { 1070c577b8a1SJoseph Chan .substreams = 1, 1071c577b8a1SJoseph Chan .channels_min = 2, 1072c577b8a1SJoseph Chan .channels_max = 2, 1073c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1074c577b8a1SJoseph Chan .ops = { 1075c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 10766b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 10779da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 10789da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1079c577b8a1SJoseph Chan }, 1080c577b8a1SJoseph Chan }; 1081c577b8a1SJoseph Chan 1082c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_capture = { 1083c577b8a1SJoseph Chan .substreams = 1, 1084c577b8a1SJoseph Chan .channels_min = 2, 1085c577b8a1SJoseph Chan .channels_max = 2, 1086c577b8a1SJoseph Chan }; 1087c577b8a1SJoseph Chan 1088c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1089c577b8a1SJoseph Chan { 1090c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1091c577b8a1SJoseph Chan int err; 1092c577b8a1SJoseph Chan int i; 1093c577b8a1SJoseph Chan 1094c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1095c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1096c577b8a1SJoseph Chan if (err < 0) 1097c577b8a1SJoseph Chan return err; 1098c577b8a1SJoseph Chan } 1099c577b8a1SJoseph Chan 1100c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1101c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 1102c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1103c577b8a1SJoseph Chan if (err < 0) 1104c577b8a1SJoseph Chan return err; 11059a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 11069a08160bSTakashi Iwai &spec->multiout); 11079a08160bSTakashi Iwai if (err < 0) 11089a08160bSTakashi Iwai return err; 11099a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1110c577b8a1SJoseph Chan } 1111c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1112c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1113c577b8a1SJoseph Chan if (err < 0) 1114c577b8a1SJoseph Chan return err; 1115c577b8a1SJoseph Chan } 111617314379SLydia Wang 111717314379SLydia Wang /* init power states */ 111817314379SLydia Wang set_jack_power_state(codec); 111917314379SLydia Wang analog_low_current_mode(codec, 1); 112017314379SLydia Wang 1121603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1122c577b8a1SJoseph Chan return 0; 1123c577b8a1SJoseph Chan } 1124c577b8a1SJoseph Chan 1125c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1126c577b8a1SJoseph Chan { 1127c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1128c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1129c577b8a1SJoseph Chan 1130c577b8a1SJoseph Chan codec->num_pcms = 1; 1131c577b8a1SJoseph Chan codec->pcm_info = info; 1132c577b8a1SJoseph Chan 1133c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 1134c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback); 1135c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; 1136c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); 1137c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 1138c577b8a1SJoseph Chan 1139c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1140c577b8a1SJoseph Chan spec->multiout.max_channels; 1141c577b8a1SJoseph Chan 1142c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1143c577b8a1SJoseph Chan codec->num_pcms++; 1144c577b8a1SJoseph Chan info++; 1145c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 11467ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1147c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1148c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1149c577b8a1SJoseph Chan *(spec->stream_digital_playback); 1150c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1151c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1152c577b8a1SJoseph Chan } 1153c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1154c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 1155c577b8a1SJoseph Chan *(spec->stream_digital_capture); 1156c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1157c577b8a1SJoseph Chan spec->dig_in_nid; 1158c577b8a1SJoseph Chan } 1159c577b8a1SJoseph Chan } 1160c577b8a1SJoseph Chan 1161c577b8a1SJoseph Chan return 0; 1162c577b8a1SJoseph Chan } 1163c577b8a1SJoseph Chan 1164c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1165c577b8a1SJoseph Chan { 1166c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1167c577b8a1SJoseph Chan 1168c577b8a1SJoseph Chan if (!spec) 1169c577b8a1SJoseph Chan return; 1170c577b8a1SJoseph Chan 1171603c4019STakashi Iwai via_free_kctls(codec); 1172c577b8a1SJoseph Chan kfree(codec->spec); 1173c577b8a1SJoseph Chan } 1174c577b8a1SJoseph Chan 117569e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 117669e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 117769e52a80SHarald Welte { 117869e52a80SHarald Welte unsigned int present; 117969e52a80SHarald Welte struct via_spec *spec = codec->spec; 118069e52a80SHarald Welte 118169e52a80SHarald Welte present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, 118269e52a80SHarald Welte AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 118369e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], 118469e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 118569e52a80SHarald Welte present ? HDA_AMP_MUTE : 0); 118669e52a80SHarald Welte } 118769e52a80SHarald Welte 118869e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 118969e52a80SHarald Welte { 119069e52a80SHarald Welte unsigned int gpio_data; 119169e52a80SHarald Welte unsigned int vol_counter; 119269e52a80SHarald Welte unsigned int vol; 119369e52a80SHarald Welte unsigned int master_vol; 119469e52a80SHarald Welte 119569e52a80SHarald Welte struct via_spec *spec = codec->spec; 119669e52a80SHarald Welte 119769e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 119869e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 119969e52a80SHarald Welte 120069e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 120169e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 120269e52a80SHarald Welte 120369e52a80SHarald Welte vol = vol_counter & 0x1F; 120469e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 120569e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 120669e52a80SHarald Welte AC_AMP_GET_INPUT); 120769e52a80SHarald Welte 120869e52a80SHarald Welte if (gpio_data == 0x02) { 120969e52a80SHarald Welte /* unmute line out */ 121069e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], 121169e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 121269e52a80SHarald Welte 121369e52a80SHarald Welte if (vol_counter & 0x20) { 121469e52a80SHarald Welte /* decrease volume */ 121569e52a80SHarald Welte if (vol > master_vol) 121669e52a80SHarald Welte vol = master_vol; 121769e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 121869e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 121969e52a80SHarald Welte master_vol-vol); 122069e52a80SHarald Welte } else { 122169e52a80SHarald Welte /* increase volume */ 122269e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 122369e52a80SHarald Welte HDA_AMP_VOLMASK, 122469e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 122569e52a80SHarald Welte (master_vol+vol)); 122669e52a80SHarald Welte } 122769e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 122869e52a80SHarald Welte /* mute line out */ 122969e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 123069e52a80SHarald Welte spec->autocfg.line_out_pins[0], 123169e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 123269e52a80SHarald Welte HDA_AMP_MUTE); 123369e52a80SHarald Welte } 123469e52a80SHarald Welte } 123569e52a80SHarald Welte 123669e52a80SHarald Welte /* unsolicited event for jack sensing */ 123769e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 123869e52a80SHarald Welte unsigned int res) 123969e52a80SHarald Welte { 124069e52a80SHarald Welte res >>= 26; 124169e52a80SHarald Welte if (res == VIA_HP_EVENT) 124269e52a80SHarald Welte via_hp_automute(codec); 124369e52a80SHarald Welte else if (res == VIA_GPIO_EVENT) 124469e52a80SHarald Welte via_gpio_control(codec); 124569e52a80SHarald Welte } 124669e52a80SHarald Welte 1247c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec) 1248c577b8a1SJoseph Chan { 1249c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 125069e52a80SHarald Welte int i; 125169e52a80SHarald Welte for (i = 0; i < spec->num_iverbs; i++) 125269e52a80SHarald Welte snd_hda_sequence_write(codec, spec->init_verbs[i]); 125369e52a80SHarald Welte 1254518bf3baSLydia Wang spec->codec_type = get_codec_type(codec); 1255518bf3baSLydia Wang if (spec->codec_type == VT1708BCE) 1256518bf3baSLydia Wang spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost 1257518bf3baSLydia Wang same */ 1258f7278fd0SJosepch Chan /* Lydia Add for EAPD enable */ 1259f7278fd0SJosepch Chan if (!spec->dig_in_nid) { /* No Digital In connection */ 126055d1d6c1STakashi Iwai if (spec->dig_in_pin) { 126155d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1262f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 126312b74c80STakashi Iwai PIN_OUT); 126455d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1265f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 1266f7278fd0SJosepch Chan } 126712b74c80STakashi Iwai } else /* enable SPDIF-input pin */ 126812b74c80STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 126912b74c80STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 1270f7278fd0SJosepch Chan 12719da29271STakashi Iwai /* assign slave outs */ 12729da29271STakashi Iwai if (spec->slave_dig_outs[0]) 12739da29271STakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 12745691ec7fSHarald Welte 1275c577b8a1SJoseph Chan return 0; 1276c577b8a1SJoseph Chan } 1277c577b8a1SJoseph Chan 1278cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1279cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 1280cb53c626STakashi Iwai { 1281cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 1282cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 1283cb53c626STakashi Iwai } 1284cb53c626STakashi Iwai #endif 1285cb53c626STakashi Iwai 1286c577b8a1SJoseph Chan /* 1287c577b8a1SJoseph Chan */ 1288c577b8a1SJoseph Chan static struct hda_codec_ops via_patch_ops = { 1289c577b8a1SJoseph Chan .build_controls = via_build_controls, 1290c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 1291c577b8a1SJoseph Chan .init = via_init, 1292c577b8a1SJoseph Chan .free = via_free, 1293cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1294cb53c626STakashi Iwai .check_power_status = via_check_power_status, 1295cb53c626STakashi Iwai #endif 1296c577b8a1SJoseph Chan }; 1297c577b8a1SJoseph Chan 1298c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */ 1299c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec, 1300c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1301c577b8a1SJoseph Chan { 1302c577b8a1SJoseph Chan int i; 1303c577b8a1SJoseph Chan hda_nid_t nid; 1304c577b8a1SJoseph Chan 1305c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; 1306c577b8a1SJoseph Chan 1307c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 1308c577b8a1SJoseph Chan 1309c577b8a1SJoseph Chan for(i = 0; i < 4; i++) { 1310c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1311c577b8a1SJoseph Chan if (nid) { 1312c577b8a1SJoseph Chan /* config dac list */ 1313c577b8a1SJoseph Chan switch (i) { 1314c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 1315c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 1316c577b8a1SJoseph Chan break; 1317c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 1318c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 1319c577b8a1SJoseph Chan break; 1320c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 1321fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 1322c577b8a1SJoseph Chan break; 1323c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 1324fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x13; 1325c577b8a1SJoseph Chan break; 1326c577b8a1SJoseph Chan } 1327c577b8a1SJoseph Chan } 1328c577b8a1SJoseph Chan } 1329c577b8a1SJoseph Chan 1330c577b8a1SJoseph Chan return 0; 1331c577b8a1SJoseph Chan } 1332c577b8a1SJoseph Chan 1333c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 1334c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, 1335c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1336c577b8a1SJoseph Chan { 1337c577b8a1SJoseph Chan char name[32]; 1338c577b8a1SJoseph Chan static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 1339c577b8a1SJoseph Chan hda_nid_t nid, nid_vol = 0; 1340c577b8a1SJoseph Chan int i, err; 1341c577b8a1SJoseph Chan 1342c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 1343c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1344c577b8a1SJoseph Chan 1345c577b8a1SJoseph Chan if (!nid) 1346c577b8a1SJoseph Chan continue; 1347c577b8a1SJoseph Chan 1348c577b8a1SJoseph Chan if (i != AUTO_SEQ_FRONT) 1349fb4cb772SHarald Welte nid_vol = 0x18 + i; 1350c577b8a1SJoseph Chan 1351c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 1352c577b8a1SJoseph Chan /* Center/LFE */ 1353c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1354c577b8a1SJoseph Chan "Center Playback Volume", 1355f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 1356f7278fd0SJosepch Chan HDA_OUTPUT)); 1357c577b8a1SJoseph Chan if (err < 0) 1358c577b8a1SJoseph Chan return err; 1359c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1360c577b8a1SJoseph Chan "LFE Playback Volume", 1361f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 1362f7278fd0SJosepch Chan HDA_OUTPUT)); 1363c577b8a1SJoseph Chan if (err < 0) 1364c577b8a1SJoseph Chan return err; 1365c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1366c577b8a1SJoseph Chan "Center Playback Switch", 1367f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 1368f7278fd0SJosepch Chan HDA_OUTPUT)); 1369c577b8a1SJoseph Chan if (err < 0) 1370c577b8a1SJoseph Chan return err; 1371c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1372c577b8a1SJoseph Chan "LFE Playback Switch", 1373f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 1374f7278fd0SJosepch Chan HDA_OUTPUT)); 1375c577b8a1SJoseph Chan if (err < 0) 1376c577b8a1SJoseph Chan return err; 1377c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT){ 1378c577b8a1SJoseph Chan /* add control to mixer index 0 */ 1379c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1380c577b8a1SJoseph Chan "Master Front Playback Volume", 1381f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x17, 3, 0, 1382f7278fd0SJosepch Chan HDA_INPUT)); 1383c577b8a1SJoseph Chan if (err < 0) 1384c577b8a1SJoseph Chan return err; 1385c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1386c577b8a1SJoseph Chan "Master Front Playback Switch", 1387f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x17, 3, 0, 1388f7278fd0SJosepch Chan HDA_INPUT)); 1389c577b8a1SJoseph Chan if (err < 0) 1390c577b8a1SJoseph Chan return err; 1391c577b8a1SJoseph Chan 1392c577b8a1SJoseph Chan /* add control to PW3 */ 1393c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1394c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1395f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1396f7278fd0SJosepch Chan HDA_OUTPUT)); 1397c577b8a1SJoseph Chan if (err < 0) 1398c577b8a1SJoseph Chan return err; 1399c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 1400c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1401f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1402f7278fd0SJosepch Chan HDA_OUTPUT)); 1403c577b8a1SJoseph Chan if (err < 0) 1404c577b8a1SJoseph Chan return err; 1405c577b8a1SJoseph Chan } else { 1406c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1407c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1408f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1409f7278fd0SJosepch Chan HDA_OUTPUT)); 1410c577b8a1SJoseph Chan if (err < 0) 1411c577b8a1SJoseph Chan return err; 1412c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 1413c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1414f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1415f7278fd0SJosepch Chan HDA_OUTPUT)); 1416c577b8a1SJoseph Chan if (err < 0) 1417c577b8a1SJoseph Chan return err; 1418c577b8a1SJoseph Chan } 1419c577b8a1SJoseph Chan } 1420c577b8a1SJoseph Chan 1421c577b8a1SJoseph Chan return 0; 1422c577b8a1SJoseph Chan } 1423c577b8a1SJoseph Chan 14240aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 14250aa62aefSHarald Welte { 14260aa62aefSHarald Welte int i; 14270aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 14280aa62aefSHarald Welte static const char *texts[] = { "OFF", "ON", NULL}; 14290aa62aefSHarald Welte 14300aa62aefSHarald Welte /* for hp mode select */ 14310aa62aefSHarald Welte i = 0; 14320aa62aefSHarald Welte while (texts[i] != NULL) { 14330aa62aefSHarald Welte imux->items[imux->num_items].label = texts[i]; 14340aa62aefSHarald Welte imux->items[imux->num_items].index = i; 14350aa62aefSHarald Welte imux->num_items++; 14360aa62aefSHarald Welte i++; 14370aa62aefSHarald Welte } 14380aa62aefSHarald Welte 14390aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 14400aa62aefSHarald Welte } 14410aa62aefSHarald Welte 1442c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 1443c577b8a1SJoseph Chan { 1444c577b8a1SJoseph Chan int err; 1445c577b8a1SJoseph Chan 1446c577b8a1SJoseph Chan if (!pin) 1447c577b8a1SJoseph Chan return 0; 1448c577b8a1SJoseph Chan 1449c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ 1450c577b8a1SJoseph Chan 1451c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1452c577b8a1SJoseph Chan "Headphone Playback Volume", 1453c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 1454c577b8a1SJoseph Chan if (err < 0) 1455c577b8a1SJoseph Chan return err; 1456c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1457c577b8a1SJoseph Chan "Headphone Playback Switch", 1458c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 1459c577b8a1SJoseph Chan if (err < 0) 1460c577b8a1SJoseph Chan return err; 1461c577b8a1SJoseph Chan 14620aa62aefSHarald Welte create_hp_imux(spec); 14630aa62aefSHarald Welte 1464c577b8a1SJoseph Chan return 0; 1465c577b8a1SJoseph Chan } 1466c577b8a1SJoseph Chan 1467c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 1468c577b8a1SJoseph Chan static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec, 1469c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1470c577b8a1SJoseph Chan { 1471c577b8a1SJoseph Chan static char *labels[] = { 1472c577b8a1SJoseph Chan "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 1473c577b8a1SJoseph Chan }; 14740aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 1475c577b8a1SJoseph Chan int i, err, idx = 0; 1476c577b8a1SJoseph Chan 1477c577b8a1SJoseph Chan /* for internal loopback recording select */ 1478c577b8a1SJoseph Chan imux->items[imux->num_items].label = "Stereo Mixer"; 1479c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 1480c577b8a1SJoseph Chan imux->num_items++; 1481c577b8a1SJoseph Chan 1482c577b8a1SJoseph Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 1483c577b8a1SJoseph Chan if (!cfg->input_pins[i]) 1484c577b8a1SJoseph Chan continue; 1485c577b8a1SJoseph Chan 1486c577b8a1SJoseph Chan switch (cfg->input_pins[i]) { 1487c577b8a1SJoseph Chan case 0x1d: /* Mic */ 1488c577b8a1SJoseph Chan idx = 2; 1489c577b8a1SJoseph Chan break; 1490c577b8a1SJoseph Chan 1491c577b8a1SJoseph Chan case 0x1e: /* Line In */ 1492c577b8a1SJoseph Chan idx = 3; 1493c577b8a1SJoseph Chan break; 1494c577b8a1SJoseph Chan 1495c577b8a1SJoseph Chan case 0x21: /* Front Mic */ 1496c577b8a1SJoseph Chan idx = 4; 1497c577b8a1SJoseph Chan break; 1498c577b8a1SJoseph Chan 1499c577b8a1SJoseph Chan case 0x24: /* CD */ 1500c577b8a1SJoseph Chan idx = 1; 1501c577b8a1SJoseph Chan break; 1502c577b8a1SJoseph Chan } 15039510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x17); 1504c577b8a1SJoseph Chan if (err < 0) 1505c577b8a1SJoseph Chan return err; 1506c577b8a1SJoseph Chan imux->items[imux->num_items].label = labels[i]; 1507c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 1508c577b8a1SJoseph Chan imux->num_items++; 1509c577b8a1SJoseph Chan } 1510c577b8a1SJoseph Chan return 0; 1511c577b8a1SJoseph Chan } 1512c577b8a1SJoseph Chan 1513cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1514cb53c626STakashi Iwai static struct hda_amp_list vt1708_loopbacks[] = { 1515cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 1516cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 1517cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 1518cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 1519cb53c626STakashi Iwai { } /* end */ 1520cb53c626STakashi Iwai }; 1521cb53c626STakashi Iwai #endif 1522cb53c626STakashi Iwai 152376d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 152476d9b0ddSHarald Welte { 152576d9b0ddSHarald Welte unsigned int def_conf; 152676d9b0ddSHarald Welte unsigned char seqassoc; 152776d9b0ddSHarald Welte 15282f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 152976d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 153076d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 153176d9b0ddSHarald Welte if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) { 153276d9b0ddSHarald Welte if (seqassoc == 0xff) { 153376d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 15342f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 153576d9b0ddSHarald Welte } 153676d9b0ddSHarald Welte } 153776d9b0ddSHarald Welte 153876d9b0ddSHarald Welte return; 153976d9b0ddSHarald Welte } 154076d9b0ddSHarald Welte 1541c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec) 1542c577b8a1SJoseph Chan { 1543c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1544c577b8a1SJoseph Chan int err; 1545c577b8a1SJoseph Chan 154676d9b0ddSHarald Welte /* Add HP and CD pin config connect bit re-config action */ 154776d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 154876d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 154976d9b0ddSHarald Welte 1550c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 1551c577b8a1SJoseph Chan if (err < 0) 1552c577b8a1SJoseph Chan return err; 1553c577b8a1SJoseph Chan err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg); 1554c577b8a1SJoseph Chan if (err < 0) 1555c577b8a1SJoseph Chan return err; 1556c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 1557c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 1558c577b8a1SJoseph Chan 1559c577b8a1SJoseph Chan err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg); 1560c577b8a1SJoseph Chan if (err < 0) 1561c577b8a1SJoseph Chan return err; 1562c577b8a1SJoseph Chan err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 1563c577b8a1SJoseph Chan if (err < 0) 1564c577b8a1SJoseph Chan return err; 1565c577b8a1SJoseph Chan err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg); 1566c577b8a1SJoseph Chan if (err < 0) 1567c577b8a1SJoseph Chan return err; 1568c577b8a1SJoseph Chan 1569c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 1570c577b8a1SJoseph Chan 15710852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 1572c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; 157355d1d6c1STakashi Iwai spec->dig_in_pin = VT1708_DIGIN_PIN; 1574c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 1575c577b8a1SJoseph Chan spec->dig_in_nid = VT1708_DIGIN_NID; 1576c577b8a1SJoseph Chan 1577603c4019STakashi Iwai if (spec->kctls.list) 1578603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 1579c577b8a1SJoseph Chan 158069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; 1581c577b8a1SJoseph Chan 15820aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 15830aa62aefSHarald Welte 1584f8fdd495SHarald Welte if (spec->hp_mux) 15850aa62aefSHarald Welte spec->mixers[spec->num_mixers++] = via_hp_mixer; 1586c577b8a1SJoseph Chan 1587c577b8a1SJoseph Chan return 1; 1588c577b8a1SJoseph Chan } 1589c577b8a1SJoseph Chan 1590c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */ 1591c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec) 1592c577b8a1SJoseph Chan { 1593c577b8a1SJoseph Chan via_init(codec); 1594c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 1595c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 1596c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 1597c577b8a1SJoseph Chan return 0; 1598c577b8a1SJoseph Chan } 1599c577b8a1SJoseph Chan 1600337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 1601337b9d02STakashi Iwai { 1602337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 1603337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 1604337b9d02STakashi Iwai unsigned int type; 1605337b9d02STakashi Iwai int i, n; 1606337b9d02STakashi Iwai 1607337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 1608337b9d02STakashi Iwai nid = spec->adc_nids[i]; 1609337b9d02STakashi Iwai while (nid) { 1610a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 16111c55d521STakashi Iwai if (type == AC_WID_PIN) 16121c55d521STakashi Iwai break; 1613337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 1614337b9d02STakashi Iwai ARRAY_SIZE(conn)); 1615337b9d02STakashi Iwai if (n <= 0) 1616337b9d02STakashi Iwai break; 1617337b9d02STakashi Iwai if (n > 1) { 1618337b9d02STakashi Iwai spec->mux_nids[i] = nid; 1619337b9d02STakashi Iwai break; 1620337b9d02STakashi Iwai } 1621337b9d02STakashi Iwai nid = conn[0]; 1622337b9d02STakashi Iwai } 1623337b9d02STakashi Iwai } 16241c55d521STakashi Iwai return 0; 1625337b9d02STakashi Iwai } 1626337b9d02STakashi Iwai 1627c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 1628c577b8a1SJoseph Chan { 1629c577b8a1SJoseph Chan struct via_spec *spec; 1630c577b8a1SJoseph Chan int err; 1631c577b8a1SJoseph Chan 1632c577b8a1SJoseph Chan /* create a codec specific record */ 1633eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1634c577b8a1SJoseph Chan if (spec == NULL) 1635c577b8a1SJoseph Chan return -ENOMEM; 1636c577b8a1SJoseph Chan 1637c577b8a1SJoseph Chan codec->spec = spec; 1638c577b8a1SJoseph Chan 1639c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 1640c577b8a1SJoseph Chan err = vt1708_parse_auto_config(codec); 1641c577b8a1SJoseph Chan if (err < 0) { 1642c577b8a1SJoseph Chan via_free(codec); 1643c577b8a1SJoseph Chan return err; 1644c577b8a1SJoseph Chan } else if (!err) { 1645c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 1646c577b8a1SJoseph Chan "from BIOS. Using genenic mode...\n"); 1647c577b8a1SJoseph Chan } 1648c577b8a1SJoseph Chan 1649c577b8a1SJoseph Chan 1650c577b8a1SJoseph Chan spec->stream_name_analog = "VT1708 Analog"; 1651c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1708_pcm_analog_playback; 1652bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 1653bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 1654bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 1655c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1708_pcm_analog_capture; 1656c577b8a1SJoseph Chan 1657c577b8a1SJoseph Chan spec->stream_name_digital = "VT1708 Digital"; 1658c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1708_pcm_digital_playback; 1659c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1708_pcm_digital_capture; 1660c577b8a1SJoseph Chan 1661c577b8a1SJoseph Chan 1662c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 1663c577b8a1SJoseph Chan spec->adc_nids = vt1708_adc_nids; 1664c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); 16650f67a611STakashi Iwai get_mux_nids(codec); 1666c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1708_capture_mixer; 1667c577b8a1SJoseph Chan spec->num_mixers++; 1668c577b8a1SJoseph Chan } 1669c577b8a1SJoseph Chan 1670c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 1671c577b8a1SJoseph Chan 1672c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 1673cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1674cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 1675cb53c626STakashi Iwai #endif 1676c577b8a1SJoseph Chan 1677c577b8a1SJoseph Chan return 0; 1678c577b8a1SJoseph Chan } 1679c577b8a1SJoseph Chan 1680c577b8a1SJoseph Chan /* capture mixer elements */ 1681c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1709_capture_mixer[] = { 1682c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), 1683c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), 1684c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), 1685c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), 1686c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), 1687c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), 1688c577b8a1SJoseph Chan { 1689c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1690c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 1691c577b8a1SJoseph Chan * So call somewhat different.. 1692c577b8a1SJoseph Chan */ 1693c577b8a1SJoseph Chan /* .name = "Capture Source", */ 1694c577b8a1SJoseph Chan .name = "Input Source", 1695c577b8a1SJoseph Chan .count = 1, 1696c577b8a1SJoseph Chan .info = via_mux_enum_info, 1697c577b8a1SJoseph Chan .get = via_mux_enum_get, 1698c577b8a1SJoseph Chan .put = via_mux_enum_put, 1699c577b8a1SJoseph Chan }, 1700c577b8a1SJoseph Chan { } /* end */ 1701c577b8a1SJoseph Chan }; 1702c577b8a1SJoseph Chan 170369e52a80SHarald Welte static struct hda_verb vt1709_uniwill_init_verbs[] = { 170469e52a80SHarald Welte {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, 170569e52a80SHarald Welte { } 170669e52a80SHarald Welte }; 170769e52a80SHarald Welte 1708c577b8a1SJoseph Chan /* 1709c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1710c577b8a1SJoseph Chan */ 1711c577b8a1SJoseph Chan static struct hda_verb vt1709_10ch_volume_init_verbs[] = { 1712c577b8a1SJoseph Chan /* 1713c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 1714c577b8a1SJoseph Chan */ 1715c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1716c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1717c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1718c577b8a1SJoseph Chan 1719c577b8a1SJoseph Chan 1720f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1721c577b8a1SJoseph Chan * mixer widget 1722c577b8a1SJoseph Chan */ 1723c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1724f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1725f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1726f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1727f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1728f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1729c577b8a1SJoseph Chan 1730c577b8a1SJoseph Chan /* 1731c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 1732c577b8a1SJoseph Chan */ 1733c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 1734c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1735c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1736c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1737c577b8a1SJoseph Chan 1738c577b8a1SJoseph Chan /* 1739c577b8a1SJoseph Chan * Unmute PW3 and PW4 1740c577b8a1SJoseph Chan */ 1741c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1742c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1743c577b8a1SJoseph Chan 1744c577b8a1SJoseph Chan /* Set input of PW4 as AOW4 */ 1745c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, 1746c577b8a1SJoseph Chan /* PW9 Output enable */ 1747c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1748c577b8a1SJoseph Chan { } 1749c577b8a1SJoseph Chan }; 1750c577b8a1SJoseph Chan 1751c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { 1752c577b8a1SJoseph Chan .substreams = 1, 1753c577b8a1SJoseph Chan .channels_min = 2, 1754c577b8a1SJoseph Chan .channels_max = 10, 1755c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 1756c577b8a1SJoseph Chan .ops = { 1757c577b8a1SJoseph Chan .open = via_playback_pcm_open, 1758c577b8a1SJoseph Chan .prepare = via_playback_pcm_prepare, 1759c577b8a1SJoseph Chan .cleanup = via_playback_pcm_cleanup 1760c577b8a1SJoseph Chan }, 1761c577b8a1SJoseph Chan }; 1762c577b8a1SJoseph Chan 1763c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { 1764c577b8a1SJoseph Chan .substreams = 1, 1765c577b8a1SJoseph Chan .channels_min = 2, 1766c577b8a1SJoseph Chan .channels_max = 6, 1767c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 1768c577b8a1SJoseph Chan .ops = { 1769c577b8a1SJoseph Chan .open = via_playback_pcm_open, 1770c577b8a1SJoseph Chan .prepare = via_playback_pcm_prepare, 1771c577b8a1SJoseph Chan .cleanup = via_playback_pcm_cleanup 1772c577b8a1SJoseph Chan }, 1773c577b8a1SJoseph Chan }; 1774c577b8a1SJoseph Chan 1775c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_analog_capture = { 1776c577b8a1SJoseph Chan .substreams = 2, 1777c577b8a1SJoseph Chan .channels_min = 2, 1778c577b8a1SJoseph Chan .channels_max = 2, 1779c577b8a1SJoseph Chan .nid = 0x14, /* NID to query formats and rates */ 1780c577b8a1SJoseph Chan .ops = { 1781c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1782c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1783c577b8a1SJoseph Chan }, 1784c577b8a1SJoseph Chan }; 1785c577b8a1SJoseph Chan 1786c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_playback = { 1787c577b8a1SJoseph Chan .substreams = 1, 1788c577b8a1SJoseph Chan .channels_min = 2, 1789c577b8a1SJoseph Chan .channels_max = 2, 1790c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1791c577b8a1SJoseph Chan .ops = { 1792c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 1793c577b8a1SJoseph Chan .close = via_dig_playback_pcm_close 1794c577b8a1SJoseph Chan }, 1795c577b8a1SJoseph Chan }; 1796c577b8a1SJoseph Chan 1797c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_capture = { 1798c577b8a1SJoseph Chan .substreams = 1, 1799c577b8a1SJoseph Chan .channels_min = 2, 1800c577b8a1SJoseph Chan .channels_max = 2, 1801c577b8a1SJoseph Chan }; 1802c577b8a1SJoseph Chan 1803c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec, 1804c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1805c577b8a1SJoseph Chan { 1806c577b8a1SJoseph Chan int i; 1807c577b8a1SJoseph Chan hda_nid_t nid; 1808c577b8a1SJoseph Chan 1809c577b8a1SJoseph Chan if (cfg->line_outs == 4) /* 10 channels */ 1810c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */ 1811c577b8a1SJoseph Chan else if (cfg->line_outs == 3) /* 6 channels */ 1812c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */ 1813c577b8a1SJoseph Chan 1814c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 1815c577b8a1SJoseph Chan 1816c577b8a1SJoseph Chan if (cfg->line_outs == 4) { /* 10 channels */ 1817c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 1818c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1819c577b8a1SJoseph Chan if (nid) { 1820c577b8a1SJoseph Chan /* config dac list */ 1821c577b8a1SJoseph Chan switch (i) { 1822c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 1823c577b8a1SJoseph Chan /* AOW0 */ 1824c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 1825c577b8a1SJoseph Chan break; 1826c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 1827c577b8a1SJoseph Chan /* AOW2 */ 1828c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 1829c577b8a1SJoseph Chan break; 1830c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 1831c577b8a1SJoseph Chan /* AOW3 */ 1832fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 1833c577b8a1SJoseph Chan break; 1834c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 1835c577b8a1SJoseph Chan /* AOW1 */ 1836fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x27; 1837c577b8a1SJoseph Chan break; 1838c577b8a1SJoseph Chan default: 1839c577b8a1SJoseph Chan break; 1840c577b8a1SJoseph Chan } 1841c577b8a1SJoseph Chan } 1842c577b8a1SJoseph Chan } 1843c577b8a1SJoseph Chan spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ 1844c577b8a1SJoseph Chan 1845c577b8a1SJoseph Chan } else if (cfg->line_outs == 3) { /* 6 channels */ 1846c577b8a1SJoseph Chan for(i = 0; i < cfg->line_outs; i++) { 1847c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1848c577b8a1SJoseph Chan if (nid) { 1849c577b8a1SJoseph Chan /* config dac list */ 1850c577b8a1SJoseph Chan switch(i) { 1851c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 1852c577b8a1SJoseph Chan /* AOW0 */ 1853c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 1854c577b8a1SJoseph Chan break; 1855c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 1856c577b8a1SJoseph Chan /* AOW2 */ 1857c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 1858c577b8a1SJoseph Chan break; 1859c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 1860c577b8a1SJoseph Chan /* AOW1 */ 1861c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x11; 1862c577b8a1SJoseph Chan break; 1863c577b8a1SJoseph Chan default: 1864c577b8a1SJoseph Chan break; 1865c577b8a1SJoseph Chan } 1866c577b8a1SJoseph Chan } 1867c577b8a1SJoseph Chan } 1868c577b8a1SJoseph Chan } 1869c577b8a1SJoseph Chan 1870c577b8a1SJoseph Chan return 0; 1871c577b8a1SJoseph Chan } 1872c577b8a1SJoseph Chan 1873c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 1874c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, 1875c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1876c577b8a1SJoseph Chan { 1877c577b8a1SJoseph Chan char name[32]; 1878c577b8a1SJoseph Chan static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 1879c577b8a1SJoseph Chan hda_nid_t nid = 0; 1880c577b8a1SJoseph Chan int i, err; 1881c577b8a1SJoseph Chan 1882c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 1883c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1884c577b8a1SJoseph Chan 1885c577b8a1SJoseph Chan if (!nid) 1886c577b8a1SJoseph Chan continue; 1887c577b8a1SJoseph Chan 1888c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 1889c577b8a1SJoseph Chan /* Center/LFE */ 1890c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1891c577b8a1SJoseph Chan "Center Playback Volume", 1892f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, 1893f7278fd0SJosepch Chan HDA_OUTPUT)); 1894c577b8a1SJoseph Chan if (err < 0) 1895c577b8a1SJoseph Chan return err; 1896c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1897c577b8a1SJoseph Chan "LFE Playback Volume", 1898f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, 1899f7278fd0SJosepch Chan HDA_OUTPUT)); 1900c577b8a1SJoseph Chan if (err < 0) 1901c577b8a1SJoseph Chan return err; 1902c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1903c577b8a1SJoseph Chan "Center Playback Switch", 1904f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, 1905f7278fd0SJosepch Chan HDA_OUTPUT)); 1906c577b8a1SJoseph Chan if (err < 0) 1907c577b8a1SJoseph Chan return err; 1908c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1909c577b8a1SJoseph Chan "LFE Playback Switch", 1910f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, 1911f7278fd0SJosepch Chan HDA_OUTPUT)); 1912c577b8a1SJoseph Chan if (err < 0) 1913c577b8a1SJoseph Chan return err; 1914c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT){ 1915c577b8a1SJoseph Chan /* add control to mixer index 0 */ 1916c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1917c577b8a1SJoseph Chan "Master Front Playback Volume", 1918f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x18, 3, 0, 1919f7278fd0SJosepch Chan HDA_INPUT)); 1920c577b8a1SJoseph Chan if (err < 0) 1921c577b8a1SJoseph Chan return err; 1922c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1923c577b8a1SJoseph Chan "Master Front Playback Switch", 1924f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x18, 3, 0, 1925f7278fd0SJosepch Chan HDA_INPUT)); 1926c577b8a1SJoseph Chan if (err < 0) 1927c577b8a1SJoseph Chan return err; 1928c577b8a1SJoseph Chan 1929c577b8a1SJoseph Chan /* add control to PW3 */ 1930c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1931c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1932f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1933f7278fd0SJosepch Chan HDA_OUTPUT)); 1934c577b8a1SJoseph Chan if (err < 0) 1935c577b8a1SJoseph Chan return err; 1936c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 1937c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1938f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1939f7278fd0SJosepch Chan HDA_OUTPUT)); 1940c577b8a1SJoseph Chan if (err < 0) 1941c577b8a1SJoseph Chan return err; 1942c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SURROUND) { 1943c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1944c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1945fb4cb772SHarald Welte HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, 1946f7278fd0SJosepch Chan HDA_OUTPUT)); 1947c577b8a1SJoseph Chan if (err < 0) 1948c577b8a1SJoseph Chan return err; 1949c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 1950c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1951fb4cb772SHarald Welte HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, 1952f7278fd0SJosepch Chan HDA_OUTPUT)); 1953c577b8a1SJoseph Chan if (err < 0) 1954c577b8a1SJoseph Chan return err; 1955c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SIDE) { 1956c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1957c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1958fb4cb772SHarald Welte HDA_COMPOSE_AMP_VAL(0x29, 3, 0, 1959f7278fd0SJosepch Chan HDA_OUTPUT)); 1960c577b8a1SJoseph Chan if (err < 0) 1961c577b8a1SJoseph Chan return err; 1962c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 1963c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1964fb4cb772SHarald Welte HDA_COMPOSE_AMP_VAL(0x29, 3, 0, 1965f7278fd0SJosepch Chan HDA_OUTPUT)); 1966c577b8a1SJoseph Chan if (err < 0) 1967c577b8a1SJoseph Chan return err; 1968c577b8a1SJoseph Chan } 1969c577b8a1SJoseph Chan } 1970c577b8a1SJoseph Chan 1971c577b8a1SJoseph Chan return 0; 1972c577b8a1SJoseph Chan } 1973c577b8a1SJoseph Chan 1974c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 1975c577b8a1SJoseph Chan { 1976c577b8a1SJoseph Chan int err; 1977c577b8a1SJoseph Chan 1978c577b8a1SJoseph Chan if (!pin) 1979c577b8a1SJoseph Chan return 0; 1980c577b8a1SJoseph Chan 1981c577b8a1SJoseph Chan if (spec->multiout.num_dacs == 5) /* 10 channels */ 1982c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1709_HP_DAC_NID; 1983c577b8a1SJoseph Chan else if (spec->multiout.num_dacs == 3) /* 6 channels */ 1984c577b8a1SJoseph Chan spec->multiout.hp_nid = 0; 1985c577b8a1SJoseph Chan 1986c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1987c577b8a1SJoseph Chan "Headphone Playback Volume", 1988c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 1989c577b8a1SJoseph Chan if (err < 0) 1990c577b8a1SJoseph Chan return err; 1991c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1992c577b8a1SJoseph Chan "Headphone Playback Switch", 1993c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 1994c577b8a1SJoseph Chan if (err < 0) 1995c577b8a1SJoseph Chan return err; 1996c577b8a1SJoseph Chan 1997c577b8a1SJoseph Chan return 0; 1998c577b8a1SJoseph Chan } 1999c577b8a1SJoseph Chan 2000c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 2001c577b8a1SJoseph Chan static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec, 2002c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2003c577b8a1SJoseph Chan { 2004c577b8a1SJoseph Chan static char *labels[] = { 2005c577b8a1SJoseph Chan "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 2006c577b8a1SJoseph Chan }; 20070aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 2008c577b8a1SJoseph Chan int i, err, idx = 0; 2009c577b8a1SJoseph Chan 2010c577b8a1SJoseph Chan /* for internal loopback recording select */ 2011c577b8a1SJoseph Chan imux->items[imux->num_items].label = "Stereo Mixer"; 2012c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 2013c577b8a1SJoseph Chan imux->num_items++; 2014c577b8a1SJoseph Chan 2015c577b8a1SJoseph Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 2016c577b8a1SJoseph Chan if (!cfg->input_pins[i]) 2017c577b8a1SJoseph Chan continue; 2018c577b8a1SJoseph Chan 2019c577b8a1SJoseph Chan switch (cfg->input_pins[i]) { 2020c577b8a1SJoseph Chan case 0x1d: /* Mic */ 2021c577b8a1SJoseph Chan idx = 2; 2022c577b8a1SJoseph Chan break; 2023c577b8a1SJoseph Chan 2024c577b8a1SJoseph Chan case 0x1e: /* Line In */ 2025c577b8a1SJoseph Chan idx = 3; 2026c577b8a1SJoseph Chan break; 2027c577b8a1SJoseph Chan 2028c577b8a1SJoseph Chan case 0x21: /* Front Mic */ 2029c577b8a1SJoseph Chan idx = 4; 2030c577b8a1SJoseph Chan break; 2031c577b8a1SJoseph Chan 2032c577b8a1SJoseph Chan case 0x23: /* CD */ 2033c577b8a1SJoseph Chan idx = 1; 2034c577b8a1SJoseph Chan break; 2035c577b8a1SJoseph Chan } 20369510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x18); 2037c577b8a1SJoseph Chan if (err < 0) 2038c577b8a1SJoseph Chan return err; 2039c577b8a1SJoseph Chan imux->items[imux->num_items].label = labels[i]; 2040c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 2041c577b8a1SJoseph Chan imux->num_items++; 2042c577b8a1SJoseph Chan } 2043c577b8a1SJoseph Chan return 0; 2044c577b8a1SJoseph Chan } 2045c577b8a1SJoseph Chan 2046c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec) 2047c577b8a1SJoseph Chan { 2048c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2049c577b8a1SJoseph Chan int err; 2050c577b8a1SJoseph Chan 2051c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2052c577b8a1SJoseph Chan if (err < 0) 2053c577b8a1SJoseph Chan return err; 2054c577b8a1SJoseph Chan err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg); 2055c577b8a1SJoseph Chan if (err < 0) 2056c577b8a1SJoseph Chan return err; 2057c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2058c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2059c577b8a1SJoseph Chan 2060c577b8a1SJoseph Chan err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg); 2061c577b8a1SJoseph Chan if (err < 0) 2062c577b8a1SJoseph Chan return err; 2063c577b8a1SJoseph Chan err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2064c577b8a1SJoseph Chan if (err < 0) 2065c577b8a1SJoseph Chan return err; 2066c577b8a1SJoseph Chan err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg); 2067c577b8a1SJoseph Chan if (err < 0) 2068c577b8a1SJoseph Chan return err; 2069c577b8a1SJoseph Chan 2070c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2071c577b8a1SJoseph Chan 20720852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2073c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; 207455d1d6c1STakashi Iwai spec->dig_in_pin = VT1709_DIGIN_PIN; 2075c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2076c577b8a1SJoseph Chan spec->dig_in_nid = VT1709_DIGIN_NID; 2077c577b8a1SJoseph Chan 2078603c4019STakashi Iwai if (spec->kctls.list) 2079603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2080c577b8a1SJoseph Chan 20810aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 2082c577b8a1SJoseph Chan 2083f8fdd495SHarald Welte if (spec->hp_mux) 2084f8fdd495SHarald Welte spec->mixers[spec->num_mixers++] = via_hp_mixer; 2085f8fdd495SHarald Welte 2086c577b8a1SJoseph Chan return 1; 2087c577b8a1SJoseph Chan } 2088c577b8a1SJoseph Chan 2089cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2090cb53c626STakashi Iwai static struct hda_amp_list vt1709_loopbacks[] = { 2091cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 2092cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 2093cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 2094cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 2095cb53c626STakashi Iwai { } /* end */ 2096cb53c626STakashi Iwai }; 2097cb53c626STakashi Iwai #endif 2098cb53c626STakashi Iwai 2099c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 2100c577b8a1SJoseph Chan { 2101c577b8a1SJoseph Chan struct via_spec *spec; 2102c577b8a1SJoseph Chan int err; 2103c577b8a1SJoseph Chan 2104c577b8a1SJoseph Chan /* create a codec specific record */ 2105eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 2106c577b8a1SJoseph Chan if (spec == NULL) 2107c577b8a1SJoseph Chan return -ENOMEM; 2108c577b8a1SJoseph Chan 2109c577b8a1SJoseph Chan codec->spec = spec; 2110c577b8a1SJoseph Chan 2111c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2112c577b8a1SJoseph Chan if (err < 0) { 2113c577b8a1SJoseph Chan via_free(codec); 2114c577b8a1SJoseph Chan return err; 2115c577b8a1SJoseph Chan } else if (!err) { 2116c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2117c577b8a1SJoseph Chan "Using genenic mode...\n"); 2118c577b8a1SJoseph Chan } 2119c577b8a1SJoseph Chan 212069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; 212169e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2122c577b8a1SJoseph Chan 2123c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 2124c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; 2125c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 2126c577b8a1SJoseph Chan 2127c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 2128c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 2129c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 2130c577b8a1SJoseph Chan 2131c577b8a1SJoseph Chan 2132c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2133c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 2134c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 2135337b9d02STakashi Iwai get_mux_nids(codec); 2136c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2137c577b8a1SJoseph Chan spec->num_mixers++; 2138c577b8a1SJoseph Chan } 2139c577b8a1SJoseph Chan 2140c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2141c577b8a1SJoseph Chan 2142c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 214369e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2144cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2145cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2146cb53c626STakashi Iwai #endif 2147c577b8a1SJoseph Chan 2148c577b8a1SJoseph Chan return 0; 2149c577b8a1SJoseph Chan } 2150c577b8a1SJoseph Chan /* 2151c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2152c577b8a1SJoseph Chan */ 2153c577b8a1SJoseph Chan static struct hda_verb vt1709_6ch_volume_init_verbs[] = { 2154c577b8a1SJoseph Chan /* 2155c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2156c577b8a1SJoseph Chan */ 2157c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2158c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2159c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2160c577b8a1SJoseph Chan 2161c577b8a1SJoseph Chan 2162c577b8a1SJoseph Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2163c577b8a1SJoseph Chan * mixer widget 2164c577b8a1SJoseph Chan */ 2165c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2166c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2167c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2168c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2169c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2170c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2171c577b8a1SJoseph Chan 2172c577b8a1SJoseph Chan /* 2173c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2174c577b8a1SJoseph Chan */ 2175c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2176c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2177c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2178c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2179c577b8a1SJoseph Chan 2180c577b8a1SJoseph Chan /* 2181c577b8a1SJoseph Chan * Unmute PW3 and PW4 2182c577b8a1SJoseph Chan */ 2183c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2184c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2185c577b8a1SJoseph Chan 2186c577b8a1SJoseph Chan /* Set input of PW4 as MW0 */ 2187c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2188c577b8a1SJoseph Chan /* PW9 Output enable */ 2189c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2190c577b8a1SJoseph Chan { } 2191c577b8a1SJoseph Chan }; 2192c577b8a1SJoseph Chan 2193c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 2194c577b8a1SJoseph Chan { 2195c577b8a1SJoseph Chan struct via_spec *spec; 2196c577b8a1SJoseph Chan int err; 2197c577b8a1SJoseph Chan 2198c577b8a1SJoseph Chan /* create a codec specific record */ 2199eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 2200c577b8a1SJoseph Chan if (spec == NULL) 2201c577b8a1SJoseph Chan return -ENOMEM; 2202c577b8a1SJoseph Chan 2203c577b8a1SJoseph Chan codec->spec = spec; 2204c577b8a1SJoseph Chan 2205c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2206c577b8a1SJoseph Chan if (err < 0) { 2207c577b8a1SJoseph Chan via_free(codec); 2208c577b8a1SJoseph Chan return err; 2209c577b8a1SJoseph Chan } else if (!err) { 2210c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2211c577b8a1SJoseph Chan "Using genenic mode...\n"); 2212c577b8a1SJoseph Chan } 2213c577b8a1SJoseph Chan 221469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; 221569e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2216c577b8a1SJoseph Chan 2217c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 2218c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; 2219c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 2220c577b8a1SJoseph Chan 2221c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 2222c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 2223c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 2224c577b8a1SJoseph Chan 2225c577b8a1SJoseph Chan 2226c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2227c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 2228c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 2229337b9d02STakashi Iwai get_mux_nids(codec); 2230c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2231c577b8a1SJoseph Chan spec->num_mixers++; 2232c577b8a1SJoseph Chan } 2233c577b8a1SJoseph Chan 2234c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2235c577b8a1SJoseph Chan 2236c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 223769e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2238cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2239cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2240cb53c626STakashi Iwai #endif 2241f7278fd0SJosepch Chan return 0; 2242f7278fd0SJosepch Chan } 2243f7278fd0SJosepch Chan 2244f7278fd0SJosepch Chan /* capture mixer elements */ 2245f7278fd0SJosepch Chan static struct snd_kcontrol_new vt1708B_capture_mixer[] = { 2246f7278fd0SJosepch Chan HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 2247f7278fd0SJosepch Chan HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 2248f7278fd0SJosepch Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 2249f7278fd0SJosepch Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 2250f7278fd0SJosepch Chan { 2251f7278fd0SJosepch Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2252f7278fd0SJosepch Chan /* The multiple "Capture Source" controls confuse alsamixer 2253f7278fd0SJosepch Chan * So call somewhat different.. 2254f7278fd0SJosepch Chan */ 2255f7278fd0SJosepch Chan /* .name = "Capture Source", */ 2256f7278fd0SJosepch Chan .name = "Input Source", 2257f7278fd0SJosepch Chan .count = 1, 2258f7278fd0SJosepch Chan .info = via_mux_enum_info, 2259f7278fd0SJosepch Chan .get = via_mux_enum_get, 2260f7278fd0SJosepch Chan .put = via_mux_enum_put, 2261f7278fd0SJosepch Chan }, 2262f7278fd0SJosepch Chan { } /* end */ 2263f7278fd0SJosepch Chan }; 2264f7278fd0SJosepch Chan /* 2265f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 2266f7278fd0SJosepch Chan */ 2267f7278fd0SJosepch Chan static struct hda_verb vt1708B_8ch_volume_init_verbs[] = { 2268f7278fd0SJosepch Chan /* 2269f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2270f7278fd0SJosepch Chan */ 2271f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2272f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2273f7278fd0SJosepch Chan 2274f7278fd0SJosepch Chan 2275f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2276f7278fd0SJosepch Chan * mixer widget 2277f7278fd0SJosepch Chan */ 2278f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2279f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2280f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2281f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2282f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2283f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2284f7278fd0SJosepch Chan 2285f7278fd0SJosepch Chan /* 2286f7278fd0SJosepch Chan * Set up output mixers 2287f7278fd0SJosepch Chan */ 2288f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2289f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2290f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2291f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2292f7278fd0SJosepch Chan 2293f7278fd0SJosepch Chan /* Setup default input to PW4 */ 2294f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, 2295f7278fd0SJosepch Chan /* PW9 Output enable */ 2296f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2297f7278fd0SJosepch Chan /* PW10 Input enable */ 2298f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2299f7278fd0SJosepch Chan { } 2300f7278fd0SJosepch Chan }; 2301f7278fd0SJosepch Chan 2302f7278fd0SJosepch Chan static struct hda_verb vt1708B_4ch_volume_init_verbs[] = { 2303f7278fd0SJosepch Chan /* 2304f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2305f7278fd0SJosepch Chan */ 2306f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2307f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2308f7278fd0SJosepch Chan 2309f7278fd0SJosepch Chan 2310f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2311f7278fd0SJosepch Chan * mixer widget 2312f7278fd0SJosepch Chan */ 2313f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2314f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2315f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2316f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2317f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2318f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2319f7278fd0SJosepch Chan 2320f7278fd0SJosepch Chan /* 2321f7278fd0SJosepch Chan * Set up output mixers 2322f7278fd0SJosepch Chan */ 2323f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2324f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2325f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2326f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2327f7278fd0SJosepch Chan 2328f7278fd0SJosepch Chan /* Setup default input of PW4 to MW0 */ 2329f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 2330f7278fd0SJosepch Chan /* PW9 Output enable */ 2331f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2332f7278fd0SJosepch Chan /* PW10 Input enable */ 2333f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2334f7278fd0SJosepch Chan { } 2335f7278fd0SJosepch Chan }; 2336f7278fd0SJosepch Chan 233769e52a80SHarald Welte static struct hda_verb vt1708B_uniwill_init_verbs[] = { 233869e52a80SHarald Welte {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, 233969e52a80SHarald Welte { } 234069e52a80SHarald Welte }; 234169e52a80SHarald Welte 234217314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo, 234317314379SLydia Wang struct hda_codec *codec, 234417314379SLydia Wang struct snd_pcm_substream *substream) 234517314379SLydia Wang { 234617314379SLydia Wang int idle = substream->pstr->substream_opened == 1 234717314379SLydia Wang && substream->ref_count == 0; 234817314379SLydia Wang 234917314379SLydia Wang analog_low_current_mode(codec, idle); 235017314379SLydia Wang return 0; 235117314379SLydia Wang } 235217314379SLydia Wang 2353f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { 23540aa62aefSHarald Welte .substreams = 2, 2355f7278fd0SJosepch Chan .channels_min = 2, 2356f7278fd0SJosepch Chan .channels_max = 8, 2357f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 2358f7278fd0SJosepch Chan .ops = { 2359f7278fd0SJosepch Chan .open = via_playback_pcm_open, 23600aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 236117314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 236217314379SLydia Wang .close = via_pcm_open_close 2363f7278fd0SJosepch Chan }, 2364f7278fd0SJosepch Chan }; 2365f7278fd0SJosepch Chan 2366f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { 23670aa62aefSHarald Welte .substreams = 2, 2368f7278fd0SJosepch Chan .channels_min = 2, 2369f7278fd0SJosepch Chan .channels_max = 4, 2370f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 2371f7278fd0SJosepch Chan .ops = { 2372f7278fd0SJosepch Chan .open = via_playback_pcm_open, 23730aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 23740aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 2375f7278fd0SJosepch Chan }, 2376f7278fd0SJosepch Chan }; 2377f7278fd0SJosepch Chan 2378f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_analog_capture = { 2379f7278fd0SJosepch Chan .substreams = 2, 2380f7278fd0SJosepch Chan .channels_min = 2, 2381f7278fd0SJosepch Chan .channels_max = 2, 2382f7278fd0SJosepch Chan .nid = 0x13, /* NID to query formats and rates */ 2383f7278fd0SJosepch Chan .ops = { 238417314379SLydia Wang .open = via_pcm_open_close, 2385f7278fd0SJosepch Chan .prepare = via_capture_pcm_prepare, 238617314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 238717314379SLydia Wang .close = via_pcm_open_close 2388f7278fd0SJosepch Chan }, 2389f7278fd0SJosepch Chan }; 2390f7278fd0SJosepch Chan 2391f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_playback = { 2392f7278fd0SJosepch Chan .substreams = 1, 2393f7278fd0SJosepch Chan .channels_min = 2, 2394f7278fd0SJosepch Chan .channels_max = 2, 2395f7278fd0SJosepch Chan /* NID is set in via_build_pcms */ 2396f7278fd0SJosepch Chan .ops = { 2397f7278fd0SJosepch Chan .open = via_dig_playback_pcm_open, 2398f7278fd0SJosepch Chan .close = via_dig_playback_pcm_close, 23999da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 24009da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 2401f7278fd0SJosepch Chan }, 2402f7278fd0SJosepch Chan }; 2403f7278fd0SJosepch Chan 2404f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_capture = { 2405f7278fd0SJosepch Chan .substreams = 1, 2406f7278fd0SJosepch Chan .channels_min = 2, 2407f7278fd0SJosepch Chan .channels_max = 2, 2408f7278fd0SJosepch Chan }; 2409f7278fd0SJosepch Chan 2410f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */ 2411f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, 2412f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 2413f7278fd0SJosepch Chan { 2414f7278fd0SJosepch Chan int i; 2415f7278fd0SJosepch Chan hda_nid_t nid; 2416f7278fd0SJosepch Chan 2417f7278fd0SJosepch Chan spec->multiout.num_dacs = cfg->line_outs; 2418f7278fd0SJosepch Chan 2419f7278fd0SJosepch Chan spec->multiout.dac_nids = spec->private_dac_nids; 2420f7278fd0SJosepch Chan 2421f7278fd0SJosepch Chan for (i = 0; i < 4; i++) { 2422f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 2423f7278fd0SJosepch Chan if (nid) { 2424f7278fd0SJosepch Chan /* config dac list */ 2425f7278fd0SJosepch Chan switch (i) { 2426f7278fd0SJosepch Chan case AUTO_SEQ_FRONT: 2427f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x10; 2428f7278fd0SJosepch Chan break; 2429f7278fd0SJosepch Chan case AUTO_SEQ_CENLFE: 2430f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x24; 2431f7278fd0SJosepch Chan break; 2432f7278fd0SJosepch Chan case AUTO_SEQ_SURROUND: 2433fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 2434f7278fd0SJosepch Chan break; 2435f7278fd0SJosepch Chan case AUTO_SEQ_SIDE: 2436fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x25; 2437f7278fd0SJosepch Chan break; 2438f7278fd0SJosepch Chan } 2439f7278fd0SJosepch Chan } 2440f7278fd0SJosepch Chan } 2441f7278fd0SJosepch Chan 2442f7278fd0SJosepch Chan return 0; 2443f7278fd0SJosepch Chan } 2444f7278fd0SJosepch Chan 2445f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */ 2446f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, 2447f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 2448f7278fd0SJosepch Chan { 2449f7278fd0SJosepch Chan char name[32]; 2450f7278fd0SJosepch Chan static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 2451fb4cb772SHarald Welte hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27}; 2452f7278fd0SJosepch Chan hda_nid_t nid, nid_vol = 0; 2453f7278fd0SJosepch Chan int i, err; 2454f7278fd0SJosepch Chan 2455f7278fd0SJosepch Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 2456f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 2457f7278fd0SJosepch Chan 2458f7278fd0SJosepch Chan if (!nid) 2459f7278fd0SJosepch Chan continue; 2460f7278fd0SJosepch Chan 2461f7278fd0SJosepch Chan nid_vol = nid_vols[i]; 2462f7278fd0SJosepch Chan 2463f7278fd0SJosepch Chan if (i == AUTO_SEQ_CENLFE) { 2464f7278fd0SJosepch Chan /* Center/LFE */ 2465f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2466f7278fd0SJosepch Chan "Center Playback Volume", 2467f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2468f7278fd0SJosepch Chan HDA_OUTPUT)); 2469f7278fd0SJosepch Chan if (err < 0) 2470f7278fd0SJosepch Chan return err; 2471f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2472f7278fd0SJosepch Chan "LFE Playback Volume", 2473f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2474f7278fd0SJosepch Chan HDA_OUTPUT)); 2475f7278fd0SJosepch Chan if (err < 0) 2476f7278fd0SJosepch Chan return err; 2477f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2478f7278fd0SJosepch Chan "Center Playback Switch", 2479f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2480f7278fd0SJosepch Chan HDA_OUTPUT)); 2481f7278fd0SJosepch Chan if (err < 0) 2482f7278fd0SJosepch Chan return err; 2483f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2484f7278fd0SJosepch Chan "LFE Playback Switch", 2485f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2486f7278fd0SJosepch Chan HDA_OUTPUT)); 2487f7278fd0SJosepch Chan if (err < 0) 2488f7278fd0SJosepch Chan return err; 2489f7278fd0SJosepch Chan } else if (i == AUTO_SEQ_FRONT) { 2490f7278fd0SJosepch Chan /* add control to mixer index 0 */ 2491f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2492f7278fd0SJosepch Chan "Master Front Playback Volume", 2493f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2494f7278fd0SJosepch Chan HDA_INPUT)); 2495f7278fd0SJosepch Chan if (err < 0) 2496f7278fd0SJosepch Chan return err; 2497f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2498f7278fd0SJosepch Chan "Master Front Playback Switch", 2499f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2500f7278fd0SJosepch Chan HDA_INPUT)); 2501f7278fd0SJosepch Chan if (err < 0) 2502f7278fd0SJosepch Chan return err; 2503f7278fd0SJosepch Chan 2504f7278fd0SJosepch Chan /* add control to PW3 */ 2505f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 2506f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2507f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2508f7278fd0SJosepch Chan HDA_OUTPUT)); 2509f7278fd0SJosepch Chan if (err < 0) 2510f7278fd0SJosepch Chan return err; 2511f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 2512f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2513f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2514f7278fd0SJosepch Chan HDA_OUTPUT)); 2515f7278fd0SJosepch Chan if (err < 0) 2516f7278fd0SJosepch Chan return err; 2517f7278fd0SJosepch Chan } else { 2518f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 2519f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2520f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2521f7278fd0SJosepch Chan HDA_OUTPUT)); 2522f7278fd0SJosepch Chan if (err < 0) 2523f7278fd0SJosepch Chan return err; 2524f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 2525f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2526f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2527f7278fd0SJosepch Chan HDA_OUTPUT)); 2528f7278fd0SJosepch Chan if (err < 0) 2529f7278fd0SJosepch Chan return err; 2530f7278fd0SJosepch Chan } 2531f7278fd0SJosepch Chan } 2532f7278fd0SJosepch Chan 2533f7278fd0SJosepch Chan return 0; 2534f7278fd0SJosepch Chan } 2535f7278fd0SJosepch Chan 2536f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 2537f7278fd0SJosepch Chan { 2538f7278fd0SJosepch Chan int err; 2539f7278fd0SJosepch Chan 2540f7278fd0SJosepch Chan if (!pin) 2541f7278fd0SJosepch Chan return 0; 2542f7278fd0SJosepch Chan 2543f7278fd0SJosepch Chan spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ 2544f7278fd0SJosepch Chan 2545f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2546f7278fd0SJosepch Chan "Headphone Playback Volume", 2547f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2548f7278fd0SJosepch Chan if (err < 0) 2549f7278fd0SJosepch Chan return err; 2550f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2551f7278fd0SJosepch Chan "Headphone Playback Switch", 2552f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2553f7278fd0SJosepch Chan if (err < 0) 2554f7278fd0SJosepch Chan return err; 2555f7278fd0SJosepch Chan 25560aa62aefSHarald Welte create_hp_imux(spec); 25570aa62aefSHarald Welte 2558f7278fd0SJosepch Chan return 0; 2559f7278fd0SJosepch Chan } 2560f7278fd0SJosepch Chan 2561f7278fd0SJosepch Chan /* create playback/capture controls for input pins */ 2562f7278fd0SJosepch Chan static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec, 2563f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 2564f7278fd0SJosepch Chan { 2565f7278fd0SJosepch Chan static char *labels[] = { 2566f7278fd0SJosepch Chan "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 2567f7278fd0SJosepch Chan }; 25680aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 2569f7278fd0SJosepch Chan int i, err, idx = 0; 2570f7278fd0SJosepch Chan 2571f7278fd0SJosepch Chan /* for internal loopback recording select */ 2572f7278fd0SJosepch Chan imux->items[imux->num_items].label = "Stereo Mixer"; 2573f7278fd0SJosepch Chan imux->items[imux->num_items].index = idx; 2574f7278fd0SJosepch Chan imux->num_items++; 2575f7278fd0SJosepch Chan 2576f7278fd0SJosepch Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 2577f7278fd0SJosepch Chan if (!cfg->input_pins[i]) 2578f7278fd0SJosepch Chan continue; 2579f7278fd0SJosepch Chan 2580f7278fd0SJosepch Chan switch (cfg->input_pins[i]) { 2581f7278fd0SJosepch Chan case 0x1a: /* Mic */ 2582f7278fd0SJosepch Chan idx = 2; 2583f7278fd0SJosepch Chan break; 2584f7278fd0SJosepch Chan 2585f7278fd0SJosepch Chan case 0x1b: /* Line In */ 2586f7278fd0SJosepch Chan idx = 3; 2587f7278fd0SJosepch Chan break; 2588f7278fd0SJosepch Chan 2589f7278fd0SJosepch Chan case 0x1e: /* Front Mic */ 2590f7278fd0SJosepch Chan idx = 4; 2591f7278fd0SJosepch Chan break; 2592f7278fd0SJosepch Chan 2593f7278fd0SJosepch Chan case 0x1f: /* CD */ 2594f7278fd0SJosepch Chan idx = 1; 2595f7278fd0SJosepch Chan break; 2596f7278fd0SJosepch Chan } 25979510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x16); 2598f7278fd0SJosepch Chan if (err < 0) 2599f7278fd0SJosepch Chan return err; 2600f7278fd0SJosepch Chan imux->items[imux->num_items].label = labels[i]; 2601f7278fd0SJosepch Chan imux->items[imux->num_items].index = idx; 2602f7278fd0SJosepch Chan imux->num_items++; 2603f7278fd0SJosepch Chan } 2604f7278fd0SJosepch Chan return 0; 2605f7278fd0SJosepch Chan } 2606f7278fd0SJosepch Chan 2607f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec) 2608f7278fd0SJosepch Chan { 2609f7278fd0SJosepch Chan struct via_spec *spec = codec->spec; 2610f7278fd0SJosepch Chan int err; 2611f7278fd0SJosepch Chan 2612f7278fd0SJosepch Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2613f7278fd0SJosepch Chan if (err < 0) 2614f7278fd0SJosepch Chan return err; 2615f7278fd0SJosepch Chan err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); 2616f7278fd0SJosepch Chan if (err < 0) 2617f7278fd0SJosepch Chan return err; 2618f7278fd0SJosepch Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2619f7278fd0SJosepch Chan return 0; /* can't find valid BIOS pin config */ 2620f7278fd0SJosepch Chan 2621f7278fd0SJosepch Chan err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); 2622f7278fd0SJosepch Chan if (err < 0) 2623f7278fd0SJosepch Chan return err; 2624f7278fd0SJosepch Chan err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2625f7278fd0SJosepch Chan if (err < 0) 2626f7278fd0SJosepch Chan return err; 2627f7278fd0SJosepch Chan err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg); 2628f7278fd0SJosepch Chan if (err < 0) 2629f7278fd0SJosepch Chan return err; 2630f7278fd0SJosepch Chan 2631f7278fd0SJosepch Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2632f7278fd0SJosepch Chan 26330852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2634f7278fd0SJosepch Chan spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; 263555d1d6c1STakashi Iwai spec->dig_in_pin = VT1708B_DIGIN_PIN; 2636f7278fd0SJosepch Chan if (spec->autocfg.dig_in_pin) 2637f7278fd0SJosepch Chan spec->dig_in_nid = VT1708B_DIGIN_NID; 2638f7278fd0SJosepch Chan 2639603c4019STakashi Iwai if (spec->kctls.list) 2640603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2641f7278fd0SJosepch Chan 26420aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 26430aa62aefSHarald Welte 2644f8fdd495SHarald Welte if (spec->hp_mux) 26450aa62aefSHarald Welte spec->mixers[spec->num_mixers++] = via_hp_mixer; 2646f7278fd0SJosepch Chan 2647f7278fd0SJosepch Chan return 1; 2648f7278fd0SJosepch Chan } 2649f7278fd0SJosepch Chan 2650f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 2651f7278fd0SJosepch Chan static struct hda_amp_list vt1708B_loopbacks[] = { 2652f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 2653f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 2654f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 2655f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 2656f7278fd0SJosepch Chan { } /* end */ 2657f7278fd0SJosepch Chan }; 2658f7278fd0SJosepch Chan #endif 2659518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 2660f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 2661f7278fd0SJosepch Chan { 2662f7278fd0SJosepch Chan struct via_spec *spec; 2663f7278fd0SJosepch Chan int err; 2664f7278fd0SJosepch Chan 2665518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 2666518bf3baSLydia Wang return patch_vt1708S(codec); 2667f7278fd0SJosepch Chan /* create a codec specific record */ 2668eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 2669f7278fd0SJosepch Chan if (spec == NULL) 2670f7278fd0SJosepch Chan return -ENOMEM; 2671f7278fd0SJosepch Chan 2672f7278fd0SJosepch Chan codec->spec = spec; 2673f7278fd0SJosepch Chan 2674f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 2675f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 2676f7278fd0SJosepch Chan if (err < 0) { 2677f7278fd0SJosepch Chan via_free(codec); 2678f7278fd0SJosepch Chan return err; 2679f7278fd0SJosepch Chan } else if (!err) { 2680f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2681f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 2682f7278fd0SJosepch Chan } 2683f7278fd0SJosepch Chan 268469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; 268569e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 2686f7278fd0SJosepch Chan 2687f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 2688f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; 2689f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 2690f7278fd0SJosepch Chan 2691f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 2692f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 2693f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 2694f7278fd0SJosepch Chan 2695f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 2696f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 2697f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 2698337b9d02STakashi Iwai get_mux_nids(codec); 2699f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 2700f7278fd0SJosepch Chan spec->num_mixers++; 2701f7278fd0SJosepch Chan } 2702f7278fd0SJosepch Chan 2703f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2704f7278fd0SJosepch Chan 2705f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 270669e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2707f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 2708f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 2709f7278fd0SJosepch Chan #endif 2710f7278fd0SJosepch Chan 2711f7278fd0SJosepch Chan return 0; 2712f7278fd0SJosepch Chan } 2713f7278fd0SJosepch Chan 2714f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 2715f7278fd0SJosepch Chan { 2716f7278fd0SJosepch Chan struct via_spec *spec; 2717f7278fd0SJosepch Chan int err; 2718f7278fd0SJosepch Chan 2719f7278fd0SJosepch Chan /* create a codec specific record */ 2720eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 2721f7278fd0SJosepch Chan if (spec == NULL) 2722f7278fd0SJosepch Chan return -ENOMEM; 2723f7278fd0SJosepch Chan 2724f7278fd0SJosepch Chan codec->spec = spec; 2725f7278fd0SJosepch Chan 2726f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 2727f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 2728f7278fd0SJosepch Chan if (err < 0) { 2729f7278fd0SJosepch Chan via_free(codec); 2730f7278fd0SJosepch Chan return err; 2731f7278fd0SJosepch Chan } else if (!err) { 2732f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2733f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 2734f7278fd0SJosepch Chan } 2735f7278fd0SJosepch Chan 273669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; 273769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 2738f7278fd0SJosepch Chan 2739f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 2740f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; 2741f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 2742f7278fd0SJosepch Chan 2743f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 2744f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 2745f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 2746f7278fd0SJosepch Chan 2747f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 2748f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 2749f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 2750337b9d02STakashi Iwai get_mux_nids(codec); 2751f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 2752f7278fd0SJosepch Chan spec->num_mixers++; 2753f7278fd0SJosepch Chan } 2754f7278fd0SJosepch Chan 2755f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2756f7278fd0SJosepch Chan 2757f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 275869e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2759f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 2760f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 2761f7278fd0SJosepch Chan #endif 2762c577b8a1SJoseph Chan 2763c577b8a1SJoseph Chan return 0; 2764c577b8a1SJoseph Chan } 2765c577b8a1SJoseph Chan 2766d949cac1SHarald Welte /* Patch for VT1708S */ 2767d949cac1SHarald Welte 2768d7426329SHarald Welte /* VT1708S software backdoor based override for buggy hardware micboost 2769d7426329SHarald Welte * setting */ 2770d7426329SHarald Welte #define MIC_BOOST_VOLUME(xname, nid) { \ 2771d7426329SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 2772d7426329SHarald Welte .name = xname, \ 2773d7426329SHarald Welte .index = 0, \ 2774d7426329SHarald Welte .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 2775d7426329SHarald Welte SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ 2776d7426329SHarald Welte SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ 2777d7426329SHarald Welte .info = mic_boost_volume_info, \ 2778d7426329SHarald Welte .get = snd_hda_mixer_amp_volume_get, \ 2779d7426329SHarald Welte .put = snd_hda_mixer_amp_volume_put, \ 2780d7426329SHarald Welte .tlv = { .c = mic_boost_tlv }, \ 2781d7426329SHarald Welte .private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT) } 2782d7426329SHarald Welte 2783d949cac1SHarald Welte /* capture mixer elements */ 2784d949cac1SHarald Welte static struct snd_kcontrol_new vt1708S_capture_mixer[] = { 2785d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 2786d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 2787d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 2788d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 2789d7426329SHarald Welte MIC_BOOST_VOLUME("Mic Boost Capture Volume", 0x1A), 2790d7426329SHarald Welte MIC_BOOST_VOLUME("Front Mic Boost Capture Volume", 0x1E), 2791d949cac1SHarald Welte { 2792d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2793d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 2794d949cac1SHarald Welte * So call somewhat different.. 2795d949cac1SHarald Welte */ 2796d949cac1SHarald Welte /* .name = "Capture Source", */ 2797d949cac1SHarald Welte .name = "Input Source", 2798d949cac1SHarald Welte .count = 1, 2799d949cac1SHarald Welte .info = via_mux_enum_info, 2800d949cac1SHarald Welte .get = via_mux_enum_get, 2801d949cac1SHarald Welte .put = via_mux_enum_put, 2802d949cac1SHarald Welte }, 2803d949cac1SHarald Welte { } /* end */ 2804d949cac1SHarald Welte }; 2805d949cac1SHarald Welte 2806d949cac1SHarald Welte static struct hda_verb vt1708S_volume_init_verbs[] = { 2807d949cac1SHarald Welte /* Unmute ADC0-1 and set the default input to mic-in */ 2808d949cac1SHarald Welte {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2809d949cac1SHarald Welte {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2810d949cac1SHarald Welte 2811d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the 2812d949cac1SHarald Welte * analog-loopback mixer widget */ 2813d949cac1SHarald Welte /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2814d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2815d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2816d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2817d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2818d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2819d949cac1SHarald Welte 2820d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 2821d949cac1SHarald Welte {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 28225691ec7fSHarald Welte /* PW9, PW10 Output enable */ 2823d949cac1SHarald Welte {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 28245691ec7fSHarald Welte {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2825d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 2826d7426329SHarald Welte {0x1, 0xf98, 0x1}, 2827d949cac1SHarald Welte { } 2828d949cac1SHarald Welte }; 2829d949cac1SHarald Welte 283069e52a80SHarald Welte static struct hda_verb vt1708S_uniwill_init_verbs[] = { 283169e52a80SHarald Welte {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, 283269e52a80SHarald Welte { } 283369e52a80SHarald Welte }; 283469e52a80SHarald Welte 2835d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_playback = { 2836d949cac1SHarald Welte .substreams = 2, 2837d949cac1SHarald Welte .channels_min = 2, 2838d949cac1SHarald Welte .channels_max = 8, 2839d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 2840d949cac1SHarald Welte .ops = { 2841d949cac1SHarald Welte .open = via_playback_pcm_open, 2842d949cac1SHarald Welte .prepare = via_playback_pcm_prepare, 284317314379SLydia Wang .cleanup = via_playback_pcm_cleanup, 284417314379SLydia Wang .close = via_pcm_open_close 2845d949cac1SHarald Welte }, 2846d949cac1SHarald Welte }; 2847d949cac1SHarald Welte 2848d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_capture = { 2849d949cac1SHarald Welte .substreams = 2, 2850d949cac1SHarald Welte .channels_min = 2, 2851d949cac1SHarald Welte .channels_max = 2, 2852d949cac1SHarald Welte .nid = 0x13, /* NID to query formats and rates */ 2853d949cac1SHarald Welte .ops = { 285417314379SLydia Wang .open = via_pcm_open_close, 2855d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 285617314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 285717314379SLydia Wang .close = via_pcm_open_close 2858d949cac1SHarald Welte }, 2859d949cac1SHarald Welte }; 2860d949cac1SHarald Welte 2861d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_digital_playback = { 28629da29271STakashi Iwai .substreams = 1, 2863d949cac1SHarald Welte .channels_min = 2, 2864d949cac1SHarald Welte .channels_max = 2, 2865d949cac1SHarald Welte /* NID is set in via_build_pcms */ 2866d949cac1SHarald Welte .ops = { 2867d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 2868d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 28699da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 28709da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 2871d949cac1SHarald Welte }, 2872d949cac1SHarald Welte }; 2873d949cac1SHarald Welte 2874d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 2875d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec, 2876d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 2877d949cac1SHarald Welte { 2878d949cac1SHarald Welte int i; 2879d949cac1SHarald Welte hda_nid_t nid; 2880d949cac1SHarald Welte 2881d949cac1SHarald Welte spec->multiout.num_dacs = cfg->line_outs; 2882d949cac1SHarald Welte 2883d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 2884d949cac1SHarald Welte 2885d949cac1SHarald Welte for (i = 0; i < 4; i++) { 2886d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 2887d949cac1SHarald Welte if (nid) { 2888d949cac1SHarald Welte /* config dac list */ 2889d949cac1SHarald Welte switch (i) { 2890d949cac1SHarald Welte case AUTO_SEQ_FRONT: 2891d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x10; 2892d949cac1SHarald Welte break; 2893d949cac1SHarald Welte case AUTO_SEQ_CENLFE: 2894d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x24; 2895d949cac1SHarald Welte break; 2896d949cac1SHarald Welte case AUTO_SEQ_SURROUND: 2897d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x11; 2898d949cac1SHarald Welte break; 2899d949cac1SHarald Welte case AUTO_SEQ_SIDE: 2900d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x25; 2901d949cac1SHarald Welte break; 2902d949cac1SHarald Welte } 2903d949cac1SHarald Welte } 2904d949cac1SHarald Welte } 2905d949cac1SHarald Welte 2906d949cac1SHarald Welte return 0; 2907d949cac1SHarald Welte } 2908d949cac1SHarald Welte 2909d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 2910d949cac1SHarald Welte static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec, 2911d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 2912d949cac1SHarald Welte { 2913d949cac1SHarald Welte char name[32]; 2914d949cac1SHarald Welte static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 2915d949cac1SHarald Welte hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25}; 2916d949cac1SHarald Welte hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27}; 2917d949cac1SHarald Welte hda_nid_t nid, nid_vol, nid_mute; 2918d949cac1SHarald Welte int i, err; 2919d949cac1SHarald Welte 2920d949cac1SHarald Welte for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 2921d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 2922d949cac1SHarald Welte 2923d949cac1SHarald Welte if (!nid) 2924d949cac1SHarald Welte continue; 2925d949cac1SHarald Welte 2926d949cac1SHarald Welte nid_vol = nid_vols[i]; 2927d949cac1SHarald Welte nid_mute = nid_mutes[i]; 2928d949cac1SHarald Welte 2929d949cac1SHarald Welte if (i == AUTO_SEQ_CENLFE) { 2930d949cac1SHarald Welte /* Center/LFE */ 2931d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2932d949cac1SHarald Welte "Center Playback Volume", 2933d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2934d949cac1SHarald Welte HDA_OUTPUT)); 2935d949cac1SHarald Welte if (err < 0) 2936d949cac1SHarald Welte return err; 2937d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2938d949cac1SHarald Welte "LFE Playback Volume", 2939d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2940d949cac1SHarald Welte HDA_OUTPUT)); 2941d949cac1SHarald Welte if (err < 0) 2942d949cac1SHarald Welte return err; 2943d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2944d949cac1SHarald Welte "Center Playback Switch", 2945d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 2946d949cac1SHarald Welte 1, 0, 2947d949cac1SHarald Welte HDA_OUTPUT)); 2948d949cac1SHarald Welte if (err < 0) 2949d949cac1SHarald Welte return err; 2950d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2951d949cac1SHarald Welte "LFE Playback Switch", 2952d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 2953d949cac1SHarald Welte 2, 0, 2954d949cac1SHarald Welte HDA_OUTPUT)); 2955d949cac1SHarald Welte if (err < 0) 2956d949cac1SHarald Welte return err; 2957d949cac1SHarald Welte } else if (i == AUTO_SEQ_FRONT) { 2958d949cac1SHarald Welte /* add control to mixer index 0 */ 2959d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2960d949cac1SHarald Welte "Master Front Playback Volume", 2961d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 2962d949cac1SHarald Welte HDA_INPUT)); 2963d949cac1SHarald Welte if (err < 0) 2964d949cac1SHarald Welte return err; 2965d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2966d949cac1SHarald Welte "Master Front Playback Switch", 2967d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 2968d949cac1SHarald Welte HDA_INPUT)); 2969d949cac1SHarald Welte if (err < 0) 2970d949cac1SHarald Welte return err; 2971d949cac1SHarald Welte 2972d949cac1SHarald Welte /* Front */ 2973d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 2974d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2975d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2976d949cac1SHarald Welte HDA_OUTPUT)); 2977d949cac1SHarald Welte if (err < 0) 2978d949cac1SHarald Welte return err; 2979d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 2980d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2981d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 2982d949cac1SHarald Welte 3, 0, 2983d949cac1SHarald Welte HDA_OUTPUT)); 2984d949cac1SHarald Welte if (err < 0) 2985d949cac1SHarald Welte return err; 2986d949cac1SHarald Welte } else { 2987d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 2988d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2989d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2990d949cac1SHarald Welte HDA_OUTPUT)); 2991d949cac1SHarald Welte if (err < 0) 2992d949cac1SHarald Welte return err; 2993d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 2994d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2995d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 2996d949cac1SHarald Welte 3, 0, 2997d949cac1SHarald Welte HDA_OUTPUT)); 2998d949cac1SHarald Welte if (err < 0) 2999d949cac1SHarald Welte return err; 3000d949cac1SHarald Welte } 3001d949cac1SHarald Welte } 3002d949cac1SHarald Welte 3003d949cac1SHarald Welte return 0; 3004d949cac1SHarald Welte } 3005d949cac1SHarald Welte 3006d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3007d949cac1SHarald Welte { 3008d949cac1SHarald Welte int err; 3009d949cac1SHarald Welte 3010d949cac1SHarald Welte if (!pin) 3011d949cac1SHarald Welte return 0; 3012d949cac1SHarald Welte 3013d949cac1SHarald Welte spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ 3014d949cac1SHarald Welte 3015d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3016d949cac1SHarald Welte "Headphone Playback Volume", 3017d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 3018d949cac1SHarald Welte if (err < 0) 3019d949cac1SHarald Welte return err; 3020d949cac1SHarald Welte 3021d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3022d949cac1SHarald Welte "Headphone Playback Switch", 3023d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3024d949cac1SHarald Welte if (err < 0) 3025d949cac1SHarald Welte return err; 3026d949cac1SHarald Welte 30270aa62aefSHarald Welte create_hp_imux(spec); 30280aa62aefSHarald Welte 3029d949cac1SHarald Welte return 0; 3030d949cac1SHarald Welte } 3031d949cac1SHarald Welte 3032d949cac1SHarald Welte /* create playback/capture controls for input pins */ 3033d949cac1SHarald Welte static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec, 3034d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3035d949cac1SHarald Welte { 3036d949cac1SHarald Welte static char *labels[] = { 3037d949cac1SHarald Welte "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 3038d949cac1SHarald Welte }; 30390aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 3040d949cac1SHarald Welte int i, err, idx = 0; 3041d949cac1SHarald Welte 3042d949cac1SHarald Welte /* for internal loopback recording select */ 3043d949cac1SHarald Welte imux->items[imux->num_items].label = "Stereo Mixer"; 3044d949cac1SHarald Welte imux->items[imux->num_items].index = 5; 3045d949cac1SHarald Welte imux->num_items++; 3046d949cac1SHarald Welte 3047d949cac1SHarald Welte for (i = 0; i < AUTO_PIN_LAST; i++) { 3048d949cac1SHarald Welte if (!cfg->input_pins[i]) 3049d949cac1SHarald Welte continue; 3050d949cac1SHarald Welte 3051d949cac1SHarald Welte switch (cfg->input_pins[i]) { 3052d949cac1SHarald Welte case 0x1a: /* Mic */ 3053d949cac1SHarald Welte idx = 2; 3054d949cac1SHarald Welte break; 3055d949cac1SHarald Welte 3056d949cac1SHarald Welte case 0x1b: /* Line In */ 3057d949cac1SHarald Welte idx = 3; 3058d949cac1SHarald Welte break; 3059d949cac1SHarald Welte 3060d949cac1SHarald Welte case 0x1e: /* Front Mic */ 3061d949cac1SHarald Welte idx = 4; 3062d949cac1SHarald Welte break; 3063d949cac1SHarald Welte 3064d949cac1SHarald Welte case 0x1f: /* CD */ 3065d949cac1SHarald Welte idx = 1; 3066d949cac1SHarald Welte break; 3067d949cac1SHarald Welte } 30689510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x16); 3069d949cac1SHarald Welte if (err < 0) 3070d949cac1SHarald Welte return err; 3071d949cac1SHarald Welte imux->items[imux->num_items].label = labels[i]; 3072d949cac1SHarald Welte imux->items[imux->num_items].index = idx-1; 3073d949cac1SHarald Welte imux->num_items++; 3074d949cac1SHarald Welte } 3075d949cac1SHarald Welte return 0; 3076d949cac1SHarald Welte } 3077d949cac1SHarald Welte 30789da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 30799da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 30809da29271STakashi Iwai { 30819da29271STakashi Iwai struct via_spec *spec = codec->spec; 30829da29271STakashi Iwai int i; 30839da29271STakashi Iwai 30849da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 30859da29271STakashi Iwai hda_nid_t nid; 30869da29271STakashi Iwai int conn; 30879da29271STakashi Iwai 30889da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 30899da29271STakashi Iwai if (!nid) 30909da29271STakashi Iwai continue; 30919da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 30929da29271STakashi Iwai if (conn < 1) 30939da29271STakashi Iwai continue; 30949da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 30959da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 30969da29271STakashi Iwai else { 30979da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 30989da29271STakashi Iwai break; /* at most two dig outs */ 30999da29271STakashi Iwai } 31009da29271STakashi Iwai } 31019da29271STakashi Iwai } 31029da29271STakashi Iwai 3103d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec) 3104d949cac1SHarald Welte { 3105d949cac1SHarald Welte struct via_spec *spec = codec->spec; 3106d949cac1SHarald Welte int err; 3107d949cac1SHarald Welte 31089da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3109d949cac1SHarald Welte if (err < 0) 3110d949cac1SHarald Welte return err; 3111d949cac1SHarald Welte err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); 3112d949cac1SHarald Welte if (err < 0) 3113d949cac1SHarald Welte return err; 3114d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3115d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 3116d949cac1SHarald Welte 3117d949cac1SHarald Welte err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg); 3118d949cac1SHarald Welte if (err < 0) 3119d949cac1SHarald Welte return err; 3120d949cac1SHarald Welte err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3121d949cac1SHarald Welte if (err < 0) 3122d949cac1SHarald Welte return err; 3123d949cac1SHarald Welte err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg); 3124d949cac1SHarald Welte if (err < 0) 3125d949cac1SHarald Welte return err; 3126d949cac1SHarald Welte 3127d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3128d949cac1SHarald Welte 31299da29271STakashi Iwai fill_dig_outs(codec); 313098aa34c0SHarald Welte 3131603c4019STakashi Iwai if (spec->kctls.list) 3132603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3133d949cac1SHarald Welte 31340aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 31350aa62aefSHarald Welte 3136f8fdd495SHarald Welte if (spec->hp_mux) 31370aa62aefSHarald Welte spec->mixers[spec->num_mixers++] = via_hp_mixer; 3138d949cac1SHarald Welte 3139d949cac1SHarald Welte return 1; 3140d949cac1SHarald Welte } 3141d949cac1SHarald Welte 3142d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3143d949cac1SHarald Welte static struct hda_amp_list vt1708S_loopbacks[] = { 3144d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 3145d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 3146d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 3147d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 3148d949cac1SHarald Welte { } /* end */ 3149d949cac1SHarald Welte }; 3150d949cac1SHarald Welte #endif 3151d949cac1SHarald Welte 3152d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 3153d949cac1SHarald Welte { 3154d949cac1SHarald Welte struct via_spec *spec; 3155d949cac1SHarald Welte int err; 3156d949cac1SHarald Welte 3157d949cac1SHarald Welte /* create a codec specific record */ 3158d949cac1SHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 3159d949cac1SHarald Welte if (spec == NULL) 3160d949cac1SHarald Welte return -ENOMEM; 3161d949cac1SHarald Welte 3162d949cac1SHarald Welte codec->spec = spec; 3163d949cac1SHarald Welte 3164d949cac1SHarald Welte /* automatic parse from the BIOS config */ 3165d949cac1SHarald Welte err = vt1708S_parse_auto_config(codec); 3166d949cac1SHarald Welte if (err < 0) { 3167d949cac1SHarald Welte via_free(codec); 3168d949cac1SHarald Welte return err; 3169d949cac1SHarald Welte } else if (!err) { 3170d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 3171d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 3172d949cac1SHarald Welte } 3173d949cac1SHarald Welte 317469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; 317569e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs; 3176d949cac1SHarald Welte 3177d949cac1SHarald Welte spec->stream_name_analog = "VT1708S Analog"; 3178d949cac1SHarald Welte spec->stream_analog_playback = &vt1708S_pcm_analog_playback; 3179d949cac1SHarald Welte spec->stream_analog_capture = &vt1708S_pcm_analog_capture; 3180d949cac1SHarald Welte 3181d949cac1SHarald Welte spec->stream_name_digital = "VT1708S Digital"; 3182d949cac1SHarald Welte spec->stream_digital_playback = &vt1708S_pcm_digital_playback; 3183d949cac1SHarald Welte 3184d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 3185d949cac1SHarald Welte spec->adc_nids = vt1708S_adc_nids; 3186d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); 3187337b9d02STakashi Iwai get_mux_nids(codec); 3188d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; 3189d949cac1SHarald Welte spec->num_mixers++; 3190d949cac1SHarald Welte } 3191d949cac1SHarald Welte 3192d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 3193d949cac1SHarald Welte 3194d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 319569e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3196d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3197d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 3198d949cac1SHarald Welte #endif 3199d949cac1SHarald Welte 3200518bf3baSLydia Wang /* correct names for VT1708BCE */ 3201518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 3202518bf3baSLydia Wang kfree(codec->chip_name); 3203518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 3204518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 3205518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 3206518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3207518bf3baSLydia Wang spec->stream_name_analog = "VT1708BCE Analog"; 3208518bf3baSLydia Wang spec->stream_name_digital = "VT1708BCE Digital"; 3209518bf3baSLydia Wang } 3210d949cac1SHarald Welte return 0; 3211d949cac1SHarald Welte } 3212d949cac1SHarald Welte 3213d949cac1SHarald Welte /* Patch for VT1702 */ 3214d949cac1SHarald Welte 3215d949cac1SHarald Welte /* capture mixer elements */ 3216d949cac1SHarald Welte static struct snd_kcontrol_new vt1702_capture_mixer[] = { 3217d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), 3218d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), 3219d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), 3220d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), 3221d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), 3222d949cac1SHarald Welte HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), 3223d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, 3224d949cac1SHarald Welte HDA_INPUT), 3225d949cac1SHarald Welte { 3226d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3227d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3228d949cac1SHarald Welte * So call somewhat different.. 3229d949cac1SHarald Welte */ 3230d949cac1SHarald Welte /* .name = "Capture Source", */ 3231d949cac1SHarald Welte .name = "Input Source", 3232d949cac1SHarald Welte .count = 1, 3233d949cac1SHarald Welte .info = via_mux_enum_info, 3234d949cac1SHarald Welte .get = via_mux_enum_get, 3235d949cac1SHarald Welte .put = via_mux_enum_put, 3236d949cac1SHarald Welte }, 3237d949cac1SHarald Welte { } /* end */ 3238d949cac1SHarald Welte }; 3239d949cac1SHarald Welte 3240d949cac1SHarald Welte static struct hda_verb vt1702_volume_init_verbs[] = { 3241d949cac1SHarald Welte /* 3242d949cac1SHarald Welte * Unmute ADC0-1 and set the default input to mic-in 3243d949cac1SHarald Welte */ 3244d949cac1SHarald Welte {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3245d949cac1SHarald Welte {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3246d949cac1SHarald Welte {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3247d949cac1SHarald Welte 3248d949cac1SHarald Welte 3249d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3250d949cac1SHarald Welte * mixer widget 3251d949cac1SHarald Welte */ 3252d949cac1SHarald Welte /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ 3253d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3254d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3255d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3256d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3257d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 3258d949cac1SHarald Welte 3259d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3260d949cac1SHarald Welte {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, 3261d949cac1SHarald Welte /* PW6 PW7 Output enable */ 3262d949cac1SHarald Welte {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3263d949cac1SHarald Welte {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3264d949cac1SHarald Welte { } 3265d949cac1SHarald Welte }; 3266d949cac1SHarald Welte 326769e52a80SHarald Welte static struct hda_verb vt1702_uniwill_init_verbs[] = { 326869e52a80SHarald Welte {0x01, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_GPIO_EVENT}, 326969e52a80SHarald Welte {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, 327069e52a80SHarald Welte { } 327169e52a80SHarald Welte }; 327269e52a80SHarald Welte 3273d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_playback = { 32740aa62aefSHarald Welte .substreams = 2, 3275d949cac1SHarald Welte .channels_min = 2, 3276d949cac1SHarald Welte .channels_max = 2, 3277d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 3278d949cac1SHarald Welte .ops = { 3279d949cac1SHarald Welte .open = via_playback_pcm_open, 32800aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 328117314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 328217314379SLydia Wang .close = via_pcm_open_close 3283d949cac1SHarald Welte }, 3284d949cac1SHarald Welte }; 3285d949cac1SHarald Welte 3286d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_capture = { 3287d949cac1SHarald Welte .substreams = 3, 3288d949cac1SHarald Welte .channels_min = 2, 3289d949cac1SHarald Welte .channels_max = 2, 3290d949cac1SHarald Welte .nid = 0x12, /* NID to query formats and rates */ 3291d949cac1SHarald Welte .ops = { 329217314379SLydia Wang .open = via_pcm_open_close, 3293d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 329417314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 329517314379SLydia Wang .close = via_pcm_open_close 3296d949cac1SHarald Welte }, 3297d949cac1SHarald Welte }; 3298d949cac1SHarald Welte 3299d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_digital_playback = { 33005691ec7fSHarald Welte .substreams = 2, 3301d949cac1SHarald Welte .channels_min = 2, 3302d949cac1SHarald Welte .channels_max = 2, 3303d949cac1SHarald Welte /* NID is set in via_build_pcms */ 3304d949cac1SHarald Welte .ops = { 3305d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 3306d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 33079da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 33089da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3309d949cac1SHarald Welte }, 3310d949cac1SHarald Welte }; 3311d949cac1SHarald Welte 3312d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 3313d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec, 3314d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3315d949cac1SHarald Welte { 3316d949cac1SHarald Welte spec->multiout.num_dacs = 1; 3317d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 3318d949cac1SHarald Welte 3319d949cac1SHarald Welte if (cfg->line_out_pins[0]) { 3320d949cac1SHarald Welte /* config dac list */ 3321d949cac1SHarald Welte spec->multiout.dac_nids[0] = 0x10; 3322d949cac1SHarald Welte } 3323d949cac1SHarald Welte 3324d949cac1SHarald Welte return 0; 3325d949cac1SHarald Welte } 3326d949cac1SHarald Welte 3327d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 3328d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, 3329d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3330d949cac1SHarald Welte { 3331d949cac1SHarald Welte int err; 3332d949cac1SHarald Welte 3333d949cac1SHarald Welte if (!cfg->line_out_pins[0]) 3334d949cac1SHarald Welte return -1; 3335d949cac1SHarald Welte 3336d949cac1SHarald Welte /* add control to mixer index 0 */ 3337d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3338d949cac1SHarald Welte "Master Front Playback Volume", 3339d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 3340d949cac1SHarald Welte if (err < 0) 3341d949cac1SHarald Welte return err; 3342d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3343d949cac1SHarald Welte "Master Front Playback Switch", 3344d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 3345d949cac1SHarald Welte if (err < 0) 3346d949cac1SHarald Welte return err; 3347d949cac1SHarald Welte 3348d949cac1SHarald Welte /* Front */ 3349d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3350d949cac1SHarald Welte "Front Playback Volume", 3351d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT)); 3352d949cac1SHarald Welte if (err < 0) 3353d949cac1SHarald Welte return err; 3354d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3355d949cac1SHarald Welte "Front Playback Switch", 3356d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT)); 3357d949cac1SHarald Welte if (err < 0) 3358d949cac1SHarald Welte return err; 3359d949cac1SHarald Welte 3360d949cac1SHarald Welte return 0; 3361d949cac1SHarald Welte } 3362d949cac1SHarald Welte 3363d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3364d949cac1SHarald Welte { 3365*0713efebSLydia Wang int err, i; 3366*0713efebSLydia Wang struct hda_input_mux *imux; 3367*0713efebSLydia Wang static const char *texts[] = { "ON", "OFF", NULL}; 3368d949cac1SHarald Welte if (!pin) 3369d949cac1SHarald Welte return 0; 3370d949cac1SHarald Welte spec->multiout.hp_nid = 0x1D; 3371d949cac1SHarald Welte 3372d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3373d949cac1SHarald Welte "Headphone Playback Volume", 3374d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT)); 3375d949cac1SHarald Welte if (err < 0) 3376d949cac1SHarald Welte return err; 3377d949cac1SHarald Welte 3378d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3379d949cac1SHarald Welte "Headphone Playback Switch", 3380d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3381d949cac1SHarald Welte if (err < 0) 3382d949cac1SHarald Welte return err; 3383d949cac1SHarald Welte 3384*0713efebSLydia Wang imux = &spec->private_imux[1]; 33850aa62aefSHarald Welte 3386*0713efebSLydia Wang /* for hp mode select */ 3387*0713efebSLydia Wang i = 0; 3388*0713efebSLydia Wang while (texts[i] != NULL) { 3389*0713efebSLydia Wang imux->items[imux->num_items].label = texts[i]; 3390*0713efebSLydia Wang imux->items[imux->num_items].index = i; 3391*0713efebSLydia Wang imux->num_items++; 3392*0713efebSLydia Wang i++; 3393*0713efebSLydia Wang } 3394*0713efebSLydia Wang 3395*0713efebSLydia Wang spec->hp_mux = &spec->private_imux[1]; 3396d949cac1SHarald Welte return 0; 3397d949cac1SHarald Welte } 3398d949cac1SHarald Welte 3399d949cac1SHarald Welte /* create playback/capture controls for input pins */ 3400d949cac1SHarald Welte static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec, 3401d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3402d949cac1SHarald Welte { 3403d949cac1SHarald Welte static char *labels[] = { 3404d949cac1SHarald Welte "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 3405d949cac1SHarald Welte }; 34060aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 3407d949cac1SHarald Welte int i, err, idx = 0; 3408d949cac1SHarald Welte 3409d949cac1SHarald Welte /* for internal loopback recording select */ 3410d949cac1SHarald Welte imux->items[imux->num_items].label = "Stereo Mixer"; 3411d949cac1SHarald Welte imux->items[imux->num_items].index = 3; 3412d949cac1SHarald Welte imux->num_items++; 3413d949cac1SHarald Welte 3414d949cac1SHarald Welte for (i = 0; i < AUTO_PIN_LAST; i++) { 3415d949cac1SHarald Welte if (!cfg->input_pins[i]) 3416d949cac1SHarald Welte continue; 3417d949cac1SHarald Welte 3418d949cac1SHarald Welte switch (cfg->input_pins[i]) { 3419d949cac1SHarald Welte case 0x14: /* Mic */ 3420d949cac1SHarald Welte idx = 1; 3421d949cac1SHarald Welte break; 3422d949cac1SHarald Welte 3423d949cac1SHarald Welte case 0x15: /* Line In */ 3424d949cac1SHarald Welte idx = 2; 3425d949cac1SHarald Welte break; 3426d949cac1SHarald Welte 3427d949cac1SHarald Welte case 0x18: /* Front Mic */ 3428d949cac1SHarald Welte idx = 3; 3429d949cac1SHarald Welte break; 3430d949cac1SHarald Welte } 34319510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x1A); 3432d949cac1SHarald Welte if (err < 0) 3433d949cac1SHarald Welte return err; 3434d949cac1SHarald Welte imux->items[imux->num_items].label = labels[i]; 3435d949cac1SHarald Welte imux->items[imux->num_items].index = idx-1; 3436d949cac1SHarald Welte imux->num_items++; 3437d949cac1SHarald Welte } 3438d949cac1SHarald Welte return 0; 3439d949cac1SHarald Welte } 3440d949cac1SHarald Welte 3441d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec) 3442d949cac1SHarald Welte { 3443d949cac1SHarald Welte struct via_spec *spec = codec->spec; 3444d949cac1SHarald Welte int err; 3445d949cac1SHarald Welte 34469da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3447d949cac1SHarald Welte if (err < 0) 3448d949cac1SHarald Welte return err; 3449d949cac1SHarald Welte err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); 3450d949cac1SHarald Welte if (err < 0) 3451d949cac1SHarald Welte return err; 3452d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3453d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 3454d949cac1SHarald Welte 3455d949cac1SHarald Welte err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg); 3456d949cac1SHarald Welte if (err < 0) 3457d949cac1SHarald Welte return err; 3458d949cac1SHarald Welte err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3459d949cac1SHarald Welte if (err < 0) 3460d949cac1SHarald Welte return err; 3461c2c02ea3SLydia Wang /* limit AA path volume to 0 dB */ 3462c2c02ea3SLydia Wang snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 3463c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 3464c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 3465c2c02ea3SLydia Wang (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 3466c2c02ea3SLydia Wang (1 << AC_AMPCAP_MUTE_SHIFT)); 3467d949cac1SHarald Welte err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg); 3468d949cac1SHarald Welte if (err < 0) 3469d949cac1SHarald Welte return err; 3470d949cac1SHarald Welte 3471d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3472d949cac1SHarald Welte 34739da29271STakashi Iwai fill_dig_outs(codec); 347498aa34c0SHarald Welte 3475603c4019STakashi Iwai if (spec->kctls.list) 3476603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3477d949cac1SHarald Welte 34780aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 34790aa62aefSHarald Welte 3480f8fdd495SHarald Welte if (spec->hp_mux) 34810aa62aefSHarald Welte spec->mixers[spec->num_mixers++] = via_hp_mixer; 3482d949cac1SHarald Welte 3483d949cac1SHarald Welte return 1; 3484d949cac1SHarald Welte } 3485d949cac1SHarald Welte 3486d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3487d949cac1SHarald Welte static struct hda_amp_list vt1702_loopbacks[] = { 3488d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 3489d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 3490d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 3491d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 3492d949cac1SHarald Welte { } /* end */ 3493d949cac1SHarald Welte }; 3494d949cac1SHarald Welte #endif 3495d949cac1SHarald Welte 3496d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 3497d949cac1SHarald Welte { 3498d949cac1SHarald Welte struct via_spec *spec; 3499d949cac1SHarald Welte int err; 3500d949cac1SHarald Welte unsigned int response; 3501d949cac1SHarald Welte unsigned char control; 3502d949cac1SHarald Welte 3503d949cac1SHarald Welte /* create a codec specific record */ 3504d949cac1SHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 3505d949cac1SHarald Welte if (spec == NULL) 3506d949cac1SHarald Welte return -ENOMEM; 3507d949cac1SHarald Welte 3508d949cac1SHarald Welte codec->spec = spec; 3509d949cac1SHarald Welte 3510d949cac1SHarald Welte /* automatic parse from the BIOS config */ 3511d949cac1SHarald Welte err = vt1702_parse_auto_config(codec); 3512d949cac1SHarald Welte if (err < 0) { 3513d949cac1SHarald Welte via_free(codec); 3514d949cac1SHarald Welte return err; 3515d949cac1SHarald Welte } else if (!err) { 3516d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 3517d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 3518d949cac1SHarald Welte } 3519d949cac1SHarald Welte 352069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; 352169e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; 3522d949cac1SHarald Welte 3523d949cac1SHarald Welte spec->stream_name_analog = "VT1702 Analog"; 3524d949cac1SHarald Welte spec->stream_analog_playback = &vt1702_pcm_analog_playback; 3525d949cac1SHarald Welte spec->stream_analog_capture = &vt1702_pcm_analog_capture; 3526d949cac1SHarald Welte 3527d949cac1SHarald Welte spec->stream_name_digital = "VT1702 Digital"; 3528d949cac1SHarald Welte spec->stream_digital_playback = &vt1702_pcm_digital_playback; 3529d949cac1SHarald Welte 3530d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 3531d949cac1SHarald Welte spec->adc_nids = vt1702_adc_nids; 3532d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids); 3533337b9d02STakashi Iwai get_mux_nids(codec); 3534d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1702_capture_mixer; 3535d949cac1SHarald Welte spec->num_mixers++; 3536d949cac1SHarald Welte } 3537d949cac1SHarald Welte 3538d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 3539d949cac1SHarald Welte 3540d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 354169e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3542d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3543d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 3544d949cac1SHarald Welte #endif 3545d949cac1SHarald Welte 3546d949cac1SHarald Welte /* Open backdoor */ 3547d949cac1SHarald Welte response = snd_hda_codec_read(codec, codec->afg, 0, 0xF8C, 0); 3548d949cac1SHarald Welte control = (unsigned char)(response & 0xff); 3549d949cac1SHarald Welte control |= 0x3; 3550d949cac1SHarald Welte snd_hda_codec_write(codec, codec->afg, 0, 0xF88, control); 3551d949cac1SHarald Welte 3552d949cac1SHarald Welte /* Enable GPIO 0&1 for volume&mute control */ 3553d949cac1SHarald Welte /* Enable GPIO 2 for DMIC-DATA */ 3554d949cac1SHarald Welte response = snd_hda_codec_read(codec, codec->afg, 0, 0xF84, 0); 3555d949cac1SHarald Welte control = (unsigned char)((response >> 16) & 0x3f); 3556d949cac1SHarald Welte snd_hda_codec_write(codec, codec->afg, 0, 0xF82, control); 3557d949cac1SHarald Welte 3558d949cac1SHarald Welte return 0; 3559d949cac1SHarald Welte } 3560d949cac1SHarald Welte 3561c577b8a1SJoseph Chan /* 3562c577b8a1SJoseph Chan * patch entries 3563c577b8a1SJoseph Chan */ 35641289e9e8STakashi Iwai static struct hda_codec_preset snd_hda_preset_via[] = { 35653218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 35663218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 35673218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 35683218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 35693218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 3570f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 35713218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 3572f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 35733218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 3574f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 35753218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 3576f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 35773218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 3578f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 35793218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 3580f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 35813218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 3582f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 35833218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 3584f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 35853218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 3586f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 35873218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 3588f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 35893218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 3590f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 35913218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 3592f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 35933218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 3594f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 35953218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 3596f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 35973218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 3598f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 35993218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 3600f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 36013218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 3602d949cac1SHarald Welte .patch = patch_vt1708S}, 36033218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 3604d949cac1SHarald Welte .patch = patch_vt1708S}, 36053218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 3606d949cac1SHarald Welte .patch = patch_vt1708S}, 36073218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 3608d949cac1SHarald Welte .patch = patch_vt1708S}, 36093218c178STakashi Iwai { .id = 0x11064397, .name = "VT1708S", 3610d949cac1SHarald Welte .patch = patch_vt1708S}, 36113218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 3612d949cac1SHarald Welte .patch = patch_vt1708S}, 36133218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 3614d949cac1SHarald Welte .patch = patch_vt1708S}, 36153218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 3616d949cac1SHarald Welte .patch = patch_vt1708S}, 36173218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 3618d949cac1SHarald Welte .patch = patch_vt1702}, 36193218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 3620d949cac1SHarald Welte .patch = patch_vt1702}, 36213218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 3622d949cac1SHarald Welte .patch = patch_vt1702}, 36233218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 3624d949cac1SHarald Welte .patch = patch_vt1702}, 36253218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 3626d949cac1SHarald Welte .patch = patch_vt1702}, 36273218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 3628d949cac1SHarald Welte .patch = patch_vt1702}, 36293218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 3630d949cac1SHarald Welte .patch = patch_vt1702}, 36313218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 3632d949cac1SHarald Welte .patch = patch_vt1702}, 3633c577b8a1SJoseph Chan {} /* terminator */ 3634c577b8a1SJoseph Chan }; 36351289e9e8STakashi Iwai 36361289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 36371289e9e8STakashi Iwai 36381289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 36391289e9e8STakashi Iwai .preset = snd_hda_preset_via, 36401289e9e8STakashi Iwai .owner = THIS_MODULE, 36411289e9e8STakashi Iwai }; 36421289e9e8STakashi Iwai 36431289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 36441289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 36451289e9e8STakashi Iwai 36461289e9e8STakashi Iwai static int __init patch_via_init(void) 36471289e9e8STakashi Iwai { 36481289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 36491289e9e8STakashi Iwai } 36501289e9e8STakashi Iwai 36511289e9e8STakashi Iwai static void __exit patch_via_exit(void) 36521289e9e8STakashi Iwai { 36531289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 36541289e9e8STakashi Iwai } 36551289e9e8STakashi Iwai 36561289e9e8STakashi Iwai module_init(patch_via_init) 36571289e9e8STakashi Iwai module_exit(patch_via_exit) 3658