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 */ 35*69e52a80SHarald Welte /* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */ 36c577b8a1SJoseph Chan /* */ 37c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 38c577b8a1SJoseph Chan 39c577b8a1SJoseph Chan 40c577b8a1SJoseph Chan #include <linux/init.h> 41c577b8a1SJoseph Chan #include <linux/delay.h> 42c577b8a1SJoseph Chan #include <linux/slab.h> 43c577b8a1SJoseph Chan #include <sound/core.h> 44c577b8a1SJoseph Chan #include "hda_codec.h" 45c577b8a1SJoseph Chan #include "hda_local.h" 463c9a3203SHarvey Harrison #include "hda_patch.h" 47c577b8a1SJoseph Chan 48c577b8a1SJoseph Chan /* amp values */ 49c577b8a1SJoseph Chan #define AMP_VAL_IDX_SHIFT 19 50c577b8a1SJoseph Chan #define AMP_VAL_IDX_MASK (0x0f<<19) 51c577b8a1SJoseph Chan 52c577b8a1SJoseph Chan #define NUM_CONTROL_ALLOC 32 53c577b8a1SJoseph Chan #define NUM_VERB_ALLOC 32 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 79c577b8a1SJoseph Chan #define IS_VT1708_VENDORID(x) ((x) >= 0x11061708 && (x) <= 0x1106170b) 80c577b8a1SJoseph Chan #define IS_VT1709_10CH_VENDORID(x) ((x) >= 0x1106e710 && (x) <= 0x1106e713) 81c577b8a1SJoseph Chan #define IS_VT1709_6CH_VENDORID(x) ((x) >= 0x1106e714 && (x) <= 0x1106e717) 82f7278fd0SJosepch Chan #define IS_VT1708B_8CH_VENDORID(x) ((x) >= 0x1106e720 && (x) <= 0x1106e723) 83f7278fd0SJosepch Chan #define IS_VT1708B_4CH_VENDORID(x) ((x) >= 0x1106e724 && (x) <= 0x1106e727) 84d949cac1SHarald Welte #define IS_VT1708S_VENDORID(x) ((x) >= 0x11060397 && (x) <= 0x11067397) 85d949cac1SHarald Welte #define IS_VT1702_VENDORID(x) ((x) >= 0x11060398 && (x) <= 0x11067398) 86c577b8a1SJoseph Chan 87*69e52a80SHarald Welte #define VIA_HP_EVENT 0x01 88*69e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 89*69e52a80SHarald Welte 90c577b8a1SJoseph Chan enum { 91c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 92c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 93c577b8a1SJoseph Chan }; 94c577b8a1SJoseph Chan 95c577b8a1SJoseph Chan enum { 96eb14a46cSHarald Welte AUTO_SEQ_FRONT = 0, 97c577b8a1SJoseph Chan AUTO_SEQ_SURROUND, 98c577b8a1SJoseph Chan AUTO_SEQ_CENLFE, 99c577b8a1SJoseph Chan AUTO_SEQ_SIDE 100c577b8a1SJoseph Chan }; 101c577b8a1SJoseph Chan 102c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_control_templates[] = { 103c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 104c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 105c577b8a1SJoseph Chan }; 106c577b8a1SJoseph Chan 107c577b8a1SJoseph Chan 108c577b8a1SJoseph Chan struct via_spec { 109c577b8a1SJoseph Chan /* codec parameterization */ 110c577b8a1SJoseph Chan struct snd_kcontrol_new *mixers[3]; 111c577b8a1SJoseph Chan unsigned int num_mixers; 112c577b8a1SJoseph Chan 113*69e52a80SHarald Welte struct hda_verb *init_verbs[5]; 114*69e52a80SHarald Welte unsigned int num_iverbs; 115c577b8a1SJoseph Chan 116c577b8a1SJoseph Chan char *stream_name_analog; 117c577b8a1SJoseph Chan struct hda_pcm_stream *stream_analog_playback; 118c577b8a1SJoseph Chan struct hda_pcm_stream *stream_analog_capture; 119c577b8a1SJoseph Chan 120c577b8a1SJoseph Chan char *stream_name_digital; 121c577b8a1SJoseph Chan struct hda_pcm_stream *stream_digital_playback; 122c577b8a1SJoseph Chan struct hda_pcm_stream *stream_digital_capture; 123c577b8a1SJoseph Chan 124c577b8a1SJoseph Chan /* playback */ 125c577b8a1SJoseph Chan struct hda_multi_out multiout; 126c577b8a1SJoseph Chan 127c577b8a1SJoseph Chan /* capture */ 128c577b8a1SJoseph Chan unsigned int num_adc_nids; 129c577b8a1SJoseph Chan hda_nid_t *adc_nids; 130c577b8a1SJoseph Chan hda_nid_t dig_in_nid; 131c577b8a1SJoseph Chan 132c577b8a1SJoseph Chan /* capture source */ 133c577b8a1SJoseph Chan const struct hda_input_mux *input_mux; 134c577b8a1SJoseph Chan unsigned int cur_mux[3]; 135c577b8a1SJoseph Chan 136c577b8a1SJoseph Chan /* PCM information */ 137c577b8a1SJoseph Chan struct hda_pcm pcm_rec[2]; 138c577b8a1SJoseph Chan 139c577b8a1SJoseph Chan /* dynamic controls, init_verbs and input_mux */ 140c577b8a1SJoseph Chan struct auto_pin_cfg autocfg; 141c577b8a1SJoseph Chan unsigned int num_kctl_alloc, num_kctl_used; 142c577b8a1SJoseph Chan struct snd_kcontrol_new *kctl_alloc; 143c577b8a1SJoseph Chan struct hda_input_mux private_imux; 14441923e44STakashi Iwai hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 145cb53c626STakashi Iwai 146cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 147cb53c626STakashi Iwai struct hda_loopback_check loopback; 148cb53c626STakashi Iwai #endif 149c577b8a1SJoseph Chan }; 150c577b8a1SJoseph Chan 151c577b8a1SJoseph Chan static hda_nid_t vt1708_adc_nids[2] = { 152c577b8a1SJoseph Chan /* ADC1-2 */ 153c577b8a1SJoseph Chan 0x15, 0x27 154c577b8a1SJoseph Chan }; 155c577b8a1SJoseph Chan 156c577b8a1SJoseph Chan static hda_nid_t vt1709_adc_nids[3] = { 157c577b8a1SJoseph Chan /* ADC1-2 */ 158c577b8a1SJoseph Chan 0x14, 0x15, 0x16 159c577b8a1SJoseph Chan }; 160c577b8a1SJoseph Chan 161f7278fd0SJosepch Chan static hda_nid_t vt1708B_adc_nids[2] = { 162f7278fd0SJosepch Chan /* ADC1-2 */ 163f7278fd0SJosepch Chan 0x13, 0x14 164f7278fd0SJosepch Chan }; 165f7278fd0SJosepch Chan 166d949cac1SHarald Welte static hda_nid_t vt1708S_adc_nids[2] = { 167d949cac1SHarald Welte /* ADC1-2 */ 168d949cac1SHarald Welte 0x13, 0x14 169d949cac1SHarald Welte }; 170d949cac1SHarald Welte 171d949cac1SHarald Welte static hda_nid_t vt1702_adc_nids[3] = { 172d949cac1SHarald Welte /* ADC1-2 */ 173d949cac1SHarald Welte 0x12, 0x20, 0x1F 174d949cac1SHarald Welte }; 175d949cac1SHarald Welte 176c577b8a1SJoseph Chan /* add dynamic controls */ 177c577b8a1SJoseph Chan static int via_add_control(struct via_spec *spec, int type, const char *name, 178c577b8a1SJoseph Chan unsigned long val) 179c577b8a1SJoseph Chan { 180c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 181c577b8a1SJoseph Chan 182c577b8a1SJoseph Chan if (spec->num_kctl_used >= spec->num_kctl_alloc) { 183c577b8a1SJoseph Chan int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; 184c577b8a1SJoseph Chan 185c577b8a1SJoseph Chan /* array + terminator */ 186c577b8a1SJoseph Chan knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); 187c577b8a1SJoseph Chan if (!knew) 188c577b8a1SJoseph Chan return -ENOMEM; 189c577b8a1SJoseph Chan if (spec->kctl_alloc) { 190c577b8a1SJoseph Chan memcpy(knew, spec->kctl_alloc, 191c577b8a1SJoseph Chan sizeof(*knew) * spec->num_kctl_alloc); 192c577b8a1SJoseph Chan kfree(spec->kctl_alloc); 193c577b8a1SJoseph Chan } 194c577b8a1SJoseph Chan spec->kctl_alloc = knew; 195c577b8a1SJoseph Chan spec->num_kctl_alloc = num; 196c577b8a1SJoseph Chan } 197c577b8a1SJoseph Chan 198c577b8a1SJoseph Chan knew = &spec->kctl_alloc[spec->num_kctl_used]; 199c577b8a1SJoseph Chan *knew = vt1708_control_templates[type]; 200c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 201c577b8a1SJoseph Chan 202c577b8a1SJoseph Chan if (!knew->name) 203c577b8a1SJoseph Chan return -ENOMEM; 204c577b8a1SJoseph Chan knew->private_value = val; 205c577b8a1SJoseph Chan spec->num_kctl_used++; 206c577b8a1SJoseph Chan return 0; 207c577b8a1SJoseph Chan } 208c577b8a1SJoseph Chan 209c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 210c577b8a1SJoseph Chan static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin, 211c577b8a1SJoseph Chan const char *ctlname, int idx, int mix_nid) 212c577b8a1SJoseph Chan { 213c577b8a1SJoseph Chan char name[32]; 214c577b8a1SJoseph Chan int err; 215c577b8a1SJoseph Chan 216c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 217c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 218c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 219c577b8a1SJoseph Chan if (err < 0) 220c577b8a1SJoseph Chan return err; 221c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 222c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 223c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 224c577b8a1SJoseph Chan if (err < 0) 225c577b8a1SJoseph Chan return err; 226c577b8a1SJoseph Chan return 0; 227c577b8a1SJoseph Chan } 228c577b8a1SJoseph Chan 229c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec, 230c577b8a1SJoseph Chan hda_nid_t nid, int pin_type, 231c577b8a1SJoseph Chan int dac_idx) 232c577b8a1SJoseph Chan { 233c577b8a1SJoseph Chan /* set as output */ 234c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 235c577b8a1SJoseph Chan pin_type); 236c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 237c577b8a1SJoseph Chan AMP_OUT_UNMUTE); 238c577b8a1SJoseph Chan } 239c577b8a1SJoseph Chan 240c577b8a1SJoseph Chan 241c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 242c577b8a1SJoseph Chan { 243c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 244c577b8a1SJoseph Chan int i; 245c577b8a1SJoseph Chan 246c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 247c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.line_out_pins[i]; 248c577b8a1SJoseph Chan if (nid) 249c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); 250c577b8a1SJoseph Chan } 251c577b8a1SJoseph Chan } 252c577b8a1SJoseph Chan 253c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 254c577b8a1SJoseph Chan { 255c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 256c577b8a1SJoseph Chan hda_nid_t pin; 257c577b8a1SJoseph Chan 258c577b8a1SJoseph Chan pin = spec->autocfg.hp_pins[0]; 259c577b8a1SJoseph Chan if (pin) /* connect to front */ 260c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); 261c577b8a1SJoseph Chan } 262c577b8a1SJoseph Chan 263c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 264c577b8a1SJoseph Chan { 265c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 266c577b8a1SJoseph Chan int i; 267c577b8a1SJoseph Chan 268c577b8a1SJoseph Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 269c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.input_pins[i]; 270c577b8a1SJoseph Chan 271c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 272c577b8a1SJoseph Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 273c577b8a1SJoseph Chan (i <= AUTO_PIN_FRONT_MIC ? 274c577b8a1SJoseph Chan PIN_VREF50 : PIN_IN)); 275c577b8a1SJoseph Chan 276c577b8a1SJoseph Chan } 277c577b8a1SJoseph Chan } 278c577b8a1SJoseph Chan /* 279c577b8a1SJoseph Chan * input MUX handling 280c577b8a1SJoseph Chan */ 281c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 282c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 283c577b8a1SJoseph Chan { 284c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 285c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 286c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 287c577b8a1SJoseph Chan } 288c577b8a1SJoseph Chan 289c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 290c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 291c577b8a1SJoseph Chan { 292c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 293c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 294c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 295c577b8a1SJoseph Chan 296c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 297c577b8a1SJoseph Chan return 0; 298c577b8a1SJoseph Chan } 299c577b8a1SJoseph Chan 300c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 301c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 302c577b8a1SJoseph Chan { 303c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 304c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 305c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 306c577b8a1SJoseph Chan unsigned int vendor_id = codec->vendor_id; 307c577b8a1SJoseph Chan 308c577b8a1SJoseph Chan /* AIW0 lydia 060801 add for correct sw0 input select */ 309c577b8a1SJoseph Chan if (IS_VT1708_VENDORID(vendor_id) && (adc_idx == 0)) 310c577b8a1SJoseph Chan return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 311c577b8a1SJoseph Chan 0x18, &spec->cur_mux[adc_idx]); 312c577b8a1SJoseph Chan else if ((IS_VT1709_10CH_VENDORID(vendor_id) || 313eb14a46cSHarald Welte IS_VT1709_6CH_VENDORID(vendor_id)) && (adc_idx == 0)) 314c577b8a1SJoseph Chan return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 315c577b8a1SJoseph Chan 0x19, &spec->cur_mux[adc_idx]); 316f7278fd0SJosepch Chan else if ((IS_VT1708B_8CH_VENDORID(vendor_id) || 317eb14a46cSHarald Welte IS_VT1708B_4CH_VENDORID(vendor_id)) && (adc_idx == 0)) 318f7278fd0SJosepch Chan return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 319f7278fd0SJosepch Chan 0x17, &spec->cur_mux[adc_idx]); 320d949cac1SHarald Welte else if (IS_VT1702_VENDORID(vendor_id) && (adc_idx == 0)) 321d949cac1SHarald Welte return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 322d949cac1SHarald Welte 0x13, &spec->cur_mux[adc_idx]); 323c577b8a1SJoseph Chan else 324c577b8a1SJoseph Chan return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 325c577b8a1SJoseph Chan spec->adc_nids[adc_idx], 326c577b8a1SJoseph Chan &spec->cur_mux[adc_idx]); 327c577b8a1SJoseph Chan } 328c577b8a1SJoseph Chan 329c577b8a1SJoseph Chan /* capture mixer elements */ 330c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_capture_mixer[] = { 331c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), 332c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), 333c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), 334c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), 335c577b8a1SJoseph Chan { 336c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 337c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 338c577b8a1SJoseph Chan * So call somewhat different.. 339c577b8a1SJoseph Chan */ 340c577b8a1SJoseph Chan /* .name = "Capture Source", */ 341c577b8a1SJoseph Chan .name = "Input Source", 342c577b8a1SJoseph Chan .count = 1, 343c577b8a1SJoseph Chan .info = via_mux_enum_info, 344c577b8a1SJoseph Chan .get = via_mux_enum_get, 345c577b8a1SJoseph Chan .put = via_mux_enum_put, 346c577b8a1SJoseph Chan }, 347c577b8a1SJoseph Chan { } /* end */ 348c577b8a1SJoseph Chan }; 349c577b8a1SJoseph Chan /* 350c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 351c577b8a1SJoseph Chan */ 352c577b8a1SJoseph Chan static struct hda_verb vt1708_volume_init_verbs[] = { 353c577b8a1SJoseph Chan /* 354c577b8a1SJoseph Chan * Unmute ADC0-1 and set the default input to mic-in 355c577b8a1SJoseph Chan */ 356c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 357c577b8a1SJoseph Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 358c577b8a1SJoseph Chan 359c577b8a1SJoseph Chan 360f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 361c577b8a1SJoseph Chan * mixer widget 362c577b8a1SJoseph Chan */ 363c577b8a1SJoseph Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 364f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 365f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 366f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 367f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 368f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 369c577b8a1SJoseph Chan 370c577b8a1SJoseph Chan /* 371c577b8a1SJoseph Chan * Set up output mixers (0x19 - 0x1b) 372c577b8a1SJoseph Chan */ 373c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 374c577b8a1SJoseph Chan {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 375c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 376c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 377c577b8a1SJoseph Chan 378c577b8a1SJoseph Chan /* Setup default input to PW4 */ 379c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, 380c577b8a1SJoseph Chan /* PW9 Output enable */ 381c577b8a1SJoseph Chan {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 382f7278fd0SJosepch Chan { } 383c577b8a1SJoseph Chan }; 384c577b8a1SJoseph Chan 385c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, 386c577b8a1SJoseph Chan struct hda_codec *codec, 387c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 388c577b8a1SJoseph Chan { 389c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 3909a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 3919a08160bSTakashi Iwai hinfo); 392c577b8a1SJoseph Chan } 393c577b8a1SJoseph Chan 394c577b8a1SJoseph Chan static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 395c577b8a1SJoseph Chan struct hda_codec *codec, 396c577b8a1SJoseph Chan unsigned int stream_tag, 397c577b8a1SJoseph Chan unsigned int format, 398c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 399c577b8a1SJoseph Chan { 400c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 401c577b8a1SJoseph Chan return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, 402c577b8a1SJoseph Chan stream_tag, format, substream); 403c577b8a1SJoseph Chan } 404c577b8a1SJoseph Chan 405c577b8a1SJoseph Chan static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 406c577b8a1SJoseph Chan struct hda_codec *codec, 407c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 408c577b8a1SJoseph Chan { 409c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 410c577b8a1SJoseph Chan return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); 411c577b8a1SJoseph Chan } 412c577b8a1SJoseph Chan 413c577b8a1SJoseph Chan /* 414c577b8a1SJoseph Chan * Digital out 415c577b8a1SJoseph Chan */ 416c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 417c577b8a1SJoseph Chan struct hda_codec *codec, 418c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 419c577b8a1SJoseph Chan { 420c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 421c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 422c577b8a1SJoseph Chan } 423c577b8a1SJoseph Chan 424c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 425c577b8a1SJoseph Chan struct hda_codec *codec, 426c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 427c577b8a1SJoseph Chan { 428c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 429c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 430c577b8a1SJoseph Chan } 431c577b8a1SJoseph Chan 4326b97eb45STakashi Iwai static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 4336b97eb45STakashi Iwai struct hda_codec *codec, 4346b97eb45STakashi Iwai unsigned int stream_tag, 4356b97eb45STakashi Iwai unsigned int format, 4366b97eb45STakashi Iwai struct snd_pcm_substream *substream) 4376b97eb45STakashi Iwai { 4386b97eb45STakashi Iwai struct via_spec *spec = codec->spec; 4396b97eb45STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 4406b97eb45STakashi Iwai stream_tag, format, substream); 4416b97eb45STakashi Iwai } 4426b97eb45STakashi Iwai 443c577b8a1SJoseph Chan /* 444c577b8a1SJoseph Chan * Analog capture 445c577b8a1SJoseph Chan */ 446c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 447c577b8a1SJoseph Chan struct hda_codec *codec, 448c577b8a1SJoseph Chan unsigned int stream_tag, 449c577b8a1SJoseph Chan unsigned int format, 450c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 451c577b8a1SJoseph Chan { 452c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 453c577b8a1SJoseph Chan 454c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 455c577b8a1SJoseph Chan stream_tag, 0, format); 456c577b8a1SJoseph Chan return 0; 457c577b8a1SJoseph Chan } 458c577b8a1SJoseph Chan 459c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 460c577b8a1SJoseph Chan struct hda_codec *codec, 461c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 462c577b8a1SJoseph Chan { 463c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 464888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 465c577b8a1SJoseph Chan return 0; 466c577b8a1SJoseph Chan } 467c577b8a1SJoseph Chan 468c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_playback = { 469c577b8a1SJoseph Chan .substreams = 1, 470c577b8a1SJoseph Chan .channels_min = 2, 471c577b8a1SJoseph Chan .channels_max = 8, 472c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 473c577b8a1SJoseph Chan .ops = { 474c577b8a1SJoseph Chan .open = via_playback_pcm_open, 475c577b8a1SJoseph Chan .prepare = via_playback_pcm_prepare, 476c577b8a1SJoseph Chan .cleanup = via_playback_pcm_cleanup 477c577b8a1SJoseph Chan }, 478c577b8a1SJoseph Chan }; 479c577b8a1SJoseph Chan 480bc9b5623STakashi Iwai static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 481bc9b5623STakashi Iwai .substreams = 1, 482bc9b5623STakashi Iwai .channels_min = 2, 483bc9b5623STakashi Iwai .channels_max = 8, 484bc9b5623STakashi Iwai .nid = 0x10, /* NID to query formats and rates */ 485bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 486bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 487bc9b5623STakashi Iwai * disable the 24bit format, so far. 488bc9b5623STakashi Iwai */ 489bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 490bc9b5623STakashi Iwai .ops = { 491bc9b5623STakashi Iwai .open = via_playback_pcm_open, 492bc9b5623STakashi Iwai .prepare = via_playback_pcm_prepare, 493bc9b5623STakashi Iwai .cleanup = via_playback_pcm_cleanup 494bc9b5623STakashi Iwai }, 495bc9b5623STakashi Iwai }; 496bc9b5623STakashi Iwai 497c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_capture = { 498c577b8a1SJoseph Chan .substreams = 2, 499c577b8a1SJoseph Chan .channels_min = 2, 500c577b8a1SJoseph Chan .channels_max = 2, 501c577b8a1SJoseph Chan .nid = 0x15, /* NID to query formats and rates */ 502c577b8a1SJoseph Chan .ops = { 503c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 504c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 505c577b8a1SJoseph Chan }, 506c577b8a1SJoseph Chan }; 507c577b8a1SJoseph Chan 508c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_playback = { 509c577b8a1SJoseph Chan .substreams = 1, 510c577b8a1SJoseph Chan .channels_min = 2, 511c577b8a1SJoseph Chan .channels_max = 2, 512c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 513c577b8a1SJoseph Chan .ops = { 514c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 5156b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 5166b97eb45STakashi Iwai .prepare = via_dig_playback_pcm_prepare 517c577b8a1SJoseph Chan }, 518c577b8a1SJoseph Chan }; 519c577b8a1SJoseph Chan 520c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_capture = { 521c577b8a1SJoseph Chan .substreams = 1, 522c577b8a1SJoseph Chan .channels_min = 2, 523c577b8a1SJoseph Chan .channels_max = 2, 524c577b8a1SJoseph Chan }; 525c577b8a1SJoseph Chan 526c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 527c577b8a1SJoseph Chan { 528c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 529c577b8a1SJoseph Chan int err; 530c577b8a1SJoseph Chan int i; 531c577b8a1SJoseph Chan 532c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 533c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 534c577b8a1SJoseph Chan if (err < 0) 535c577b8a1SJoseph Chan return err; 536c577b8a1SJoseph Chan } 537c577b8a1SJoseph Chan 538c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 539c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 540c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 541c577b8a1SJoseph Chan if (err < 0) 542c577b8a1SJoseph Chan return err; 5439a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 5449a08160bSTakashi Iwai &spec->multiout); 5459a08160bSTakashi Iwai if (err < 0) 5469a08160bSTakashi Iwai return err; 5479a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 548c577b8a1SJoseph Chan } 549c577b8a1SJoseph Chan if (spec->dig_in_nid) { 550c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 551c577b8a1SJoseph Chan if (err < 0) 552c577b8a1SJoseph Chan return err; 553c577b8a1SJoseph Chan } 554c577b8a1SJoseph Chan return 0; 555c577b8a1SJoseph Chan } 556c577b8a1SJoseph Chan 557c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 558c577b8a1SJoseph Chan { 559c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 560c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 561c577b8a1SJoseph Chan 562c577b8a1SJoseph Chan codec->num_pcms = 1; 563c577b8a1SJoseph Chan codec->pcm_info = info; 564c577b8a1SJoseph Chan 565c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 566c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback); 567c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; 568c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); 569c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 570c577b8a1SJoseph Chan 571c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 572c577b8a1SJoseph Chan spec->multiout.max_channels; 573c577b8a1SJoseph Chan 574c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 575c577b8a1SJoseph Chan codec->num_pcms++; 576c577b8a1SJoseph Chan info++; 577c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 5787ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 579c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 580c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 581c577b8a1SJoseph Chan *(spec->stream_digital_playback); 582c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 583c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 584c577b8a1SJoseph Chan } 585c577b8a1SJoseph Chan if (spec->dig_in_nid) { 586c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 587c577b8a1SJoseph Chan *(spec->stream_digital_capture); 588c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 589c577b8a1SJoseph Chan spec->dig_in_nid; 590c577b8a1SJoseph Chan } 591c577b8a1SJoseph Chan } 592c577b8a1SJoseph Chan 593c577b8a1SJoseph Chan return 0; 594c577b8a1SJoseph Chan } 595c577b8a1SJoseph Chan 596c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 597c577b8a1SJoseph Chan { 598c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 599c577b8a1SJoseph Chan unsigned int i; 600c577b8a1SJoseph Chan 601c577b8a1SJoseph Chan if (!spec) 602c577b8a1SJoseph Chan return; 603c577b8a1SJoseph Chan 604c577b8a1SJoseph Chan if (spec->kctl_alloc) { 605c577b8a1SJoseph Chan for (i = 0; i < spec->num_kctl_used; i++) 606c577b8a1SJoseph Chan kfree(spec->kctl_alloc[i].name); 607c577b8a1SJoseph Chan kfree(spec->kctl_alloc); 608c577b8a1SJoseph Chan } 609c577b8a1SJoseph Chan 610c577b8a1SJoseph Chan kfree(codec->spec); 611c577b8a1SJoseph Chan } 612c577b8a1SJoseph Chan 613*69e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 614*69e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 615*69e52a80SHarald Welte { 616*69e52a80SHarald Welte unsigned int present; 617*69e52a80SHarald Welte struct via_spec *spec = codec->spec; 618*69e52a80SHarald Welte 619*69e52a80SHarald Welte present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, 620*69e52a80SHarald Welte AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 621*69e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], 622*69e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 623*69e52a80SHarald Welte present ? HDA_AMP_MUTE : 0); 624*69e52a80SHarald Welte } 625*69e52a80SHarald Welte 626*69e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 627*69e52a80SHarald Welte { 628*69e52a80SHarald Welte unsigned int gpio_data; 629*69e52a80SHarald Welte unsigned int vol_counter; 630*69e52a80SHarald Welte unsigned int vol; 631*69e52a80SHarald Welte unsigned int master_vol; 632*69e52a80SHarald Welte 633*69e52a80SHarald Welte struct via_spec *spec = codec->spec; 634*69e52a80SHarald Welte 635*69e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 636*69e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 637*69e52a80SHarald Welte 638*69e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 639*69e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 640*69e52a80SHarald Welte 641*69e52a80SHarald Welte vol = vol_counter & 0x1F; 642*69e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 643*69e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 644*69e52a80SHarald Welte AC_AMP_GET_INPUT); 645*69e52a80SHarald Welte 646*69e52a80SHarald Welte if (gpio_data == 0x02) { 647*69e52a80SHarald Welte /* unmute line out */ 648*69e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], 649*69e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 650*69e52a80SHarald Welte 651*69e52a80SHarald Welte if (vol_counter & 0x20) { 652*69e52a80SHarald Welte /* decrease volume */ 653*69e52a80SHarald Welte if (vol > master_vol) 654*69e52a80SHarald Welte vol = master_vol; 655*69e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 656*69e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 657*69e52a80SHarald Welte master_vol-vol); 658*69e52a80SHarald Welte } else { 659*69e52a80SHarald Welte /* increase volume */ 660*69e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 661*69e52a80SHarald Welte HDA_AMP_VOLMASK, 662*69e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 663*69e52a80SHarald Welte (master_vol+vol)); 664*69e52a80SHarald Welte } 665*69e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 666*69e52a80SHarald Welte /* mute line out */ 667*69e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 668*69e52a80SHarald Welte spec->autocfg.line_out_pins[0], 669*69e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 670*69e52a80SHarald Welte HDA_AMP_MUTE); 671*69e52a80SHarald Welte } 672*69e52a80SHarald Welte } 673*69e52a80SHarald Welte 674*69e52a80SHarald Welte /* unsolicited event for jack sensing */ 675*69e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 676*69e52a80SHarald Welte unsigned int res) 677*69e52a80SHarald Welte { 678*69e52a80SHarald Welte res >>= 26; 679*69e52a80SHarald Welte if (res == VIA_HP_EVENT) 680*69e52a80SHarald Welte via_hp_automute(codec); 681*69e52a80SHarald Welte else if (res == VIA_GPIO_EVENT) 682*69e52a80SHarald Welte via_gpio_control(codec); 683*69e52a80SHarald Welte } 684*69e52a80SHarald Welte 685c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec) 686c577b8a1SJoseph Chan { 687c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 688*69e52a80SHarald Welte int i; 689*69e52a80SHarald Welte for (i = 0; i < spec->num_iverbs; i++) 690*69e52a80SHarald Welte snd_hda_sequence_write(codec, spec->init_verbs[i]); 691*69e52a80SHarald Welte 692f7278fd0SJosepch Chan /* Lydia Add for EAPD enable */ 693f7278fd0SJosepch Chan if (!spec->dig_in_nid) { /* No Digital In connection */ 694f7278fd0SJosepch Chan if (IS_VT1708_VENDORID(codec->vendor_id)) { 695f7278fd0SJosepch Chan snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0, 696f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 69712b74c80STakashi Iwai PIN_OUT); 698f7278fd0SJosepch Chan snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0, 699f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 700f7278fd0SJosepch Chan } else if (IS_VT1709_10CH_VENDORID(codec->vendor_id) || 701f7278fd0SJosepch Chan IS_VT1709_6CH_VENDORID(codec->vendor_id)) { 702f7278fd0SJosepch Chan snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0, 703f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 70412b74c80STakashi Iwai PIN_OUT); 705f7278fd0SJosepch Chan snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0, 706f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 707f7278fd0SJosepch Chan } else if (IS_VT1708B_8CH_VENDORID(codec->vendor_id) || 708f7278fd0SJosepch Chan IS_VT1708B_4CH_VENDORID(codec->vendor_id)) { 709f7278fd0SJosepch Chan snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0, 710f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 71112b74c80STakashi Iwai PIN_OUT); 712f7278fd0SJosepch Chan snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0, 713f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 714f7278fd0SJosepch Chan } 71512b74c80STakashi Iwai } else /* enable SPDIF-input pin */ 71612b74c80STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 71712b74c80STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 718f7278fd0SJosepch Chan 719c577b8a1SJoseph Chan return 0; 720c577b8a1SJoseph Chan } 721c577b8a1SJoseph Chan 722cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 723cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 724cb53c626STakashi Iwai { 725cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 726cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 727cb53c626STakashi Iwai } 728cb53c626STakashi Iwai #endif 729cb53c626STakashi Iwai 730c577b8a1SJoseph Chan /* 731c577b8a1SJoseph Chan */ 732c577b8a1SJoseph Chan static struct hda_codec_ops via_patch_ops = { 733c577b8a1SJoseph Chan .build_controls = via_build_controls, 734c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 735c577b8a1SJoseph Chan .init = via_init, 736c577b8a1SJoseph Chan .free = via_free, 737cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 738cb53c626STakashi Iwai .check_power_status = via_check_power_status, 739cb53c626STakashi Iwai #endif 740c577b8a1SJoseph Chan }; 741c577b8a1SJoseph Chan 742c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */ 743c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec, 744c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 745c577b8a1SJoseph Chan { 746c577b8a1SJoseph Chan int i; 747c577b8a1SJoseph Chan hda_nid_t nid; 748c577b8a1SJoseph Chan 749c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; 750c577b8a1SJoseph Chan 751c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 752c577b8a1SJoseph Chan 753c577b8a1SJoseph Chan for(i = 0; i < 4; i++) { 754c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 755c577b8a1SJoseph Chan if (nid) { 756c577b8a1SJoseph Chan /* config dac list */ 757c577b8a1SJoseph Chan switch (i) { 758c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 759c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 760c577b8a1SJoseph Chan break; 761c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 762c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 763c577b8a1SJoseph Chan break; 764c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 765fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 766c577b8a1SJoseph Chan break; 767c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 768fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x13; 769c577b8a1SJoseph Chan break; 770c577b8a1SJoseph Chan } 771c577b8a1SJoseph Chan } 772c577b8a1SJoseph Chan } 773c577b8a1SJoseph Chan 774c577b8a1SJoseph Chan return 0; 775c577b8a1SJoseph Chan } 776c577b8a1SJoseph Chan 777c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 778c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, 779c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 780c577b8a1SJoseph Chan { 781c577b8a1SJoseph Chan char name[32]; 782c577b8a1SJoseph Chan static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 783c577b8a1SJoseph Chan hda_nid_t nid, nid_vol = 0; 784c577b8a1SJoseph Chan int i, err; 785c577b8a1SJoseph Chan 786c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 787c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 788c577b8a1SJoseph Chan 789c577b8a1SJoseph Chan if (!nid) 790c577b8a1SJoseph Chan continue; 791c577b8a1SJoseph Chan 792c577b8a1SJoseph Chan if (i != AUTO_SEQ_FRONT) 793fb4cb772SHarald Welte nid_vol = 0x18 + i; 794c577b8a1SJoseph Chan 795c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 796c577b8a1SJoseph Chan /* Center/LFE */ 797c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 798c577b8a1SJoseph Chan "Center Playback Volume", 799f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 800f7278fd0SJosepch Chan HDA_OUTPUT)); 801c577b8a1SJoseph Chan if (err < 0) 802c577b8a1SJoseph Chan return err; 803c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 804c577b8a1SJoseph Chan "LFE Playback Volume", 805f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 806f7278fd0SJosepch Chan HDA_OUTPUT)); 807c577b8a1SJoseph Chan if (err < 0) 808c577b8a1SJoseph Chan return err; 809c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 810c577b8a1SJoseph Chan "Center Playback Switch", 811f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 812f7278fd0SJosepch Chan HDA_OUTPUT)); 813c577b8a1SJoseph Chan if (err < 0) 814c577b8a1SJoseph Chan return err; 815c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 816c577b8a1SJoseph Chan "LFE Playback Switch", 817f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 818f7278fd0SJosepch Chan HDA_OUTPUT)); 819c577b8a1SJoseph Chan if (err < 0) 820c577b8a1SJoseph Chan return err; 821c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT){ 822c577b8a1SJoseph Chan /* add control to mixer index 0 */ 823c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 824c577b8a1SJoseph Chan "Master Front Playback Volume", 825f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x17, 3, 0, 826f7278fd0SJosepch Chan HDA_INPUT)); 827c577b8a1SJoseph Chan if (err < 0) 828c577b8a1SJoseph Chan return err; 829c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 830c577b8a1SJoseph Chan "Master Front Playback Switch", 831f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x17, 3, 0, 832f7278fd0SJosepch Chan HDA_INPUT)); 833c577b8a1SJoseph Chan if (err < 0) 834c577b8a1SJoseph Chan return err; 835c577b8a1SJoseph Chan 836c577b8a1SJoseph Chan /* add control to PW3 */ 837c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 838c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 839f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 840f7278fd0SJosepch Chan HDA_OUTPUT)); 841c577b8a1SJoseph Chan if (err < 0) 842c577b8a1SJoseph Chan return err; 843c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 844c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 845f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 846f7278fd0SJosepch Chan HDA_OUTPUT)); 847c577b8a1SJoseph Chan if (err < 0) 848c577b8a1SJoseph Chan return err; 849c577b8a1SJoseph Chan } else { 850c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 851c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 852f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 853f7278fd0SJosepch Chan HDA_OUTPUT)); 854c577b8a1SJoseph Chan if (err < 0) 855c577b8a1SJoseph Chan return err; 856c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 857c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 858f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 859f7278fd0SJosepch Chan HDA_OUTPUT)); 860c577b8a1SJoseph Chan if (err < 0) 861c577b8a1SJoseph Chan return err; 862c577b8a1SJoseph Chan } 863c577b8a1SJoseph Chan } 864c577b8a1SJoseph Chan 865c577b8a1SJoseph Chan return 0; 866c577b8a1SJoseph Chan } 867c577b8a1SJoseph Chan 868c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 869c577b8a1SJoseph Chan { 870c577b8a1SJoseph Chan int err; 871c577b8a1SJoseph Chan 872c577b8a1SJoseph Chan if (!pin) 873c577b8a1SJoseph Chan return 0; 874c577b8a1SJoseph Chan 875c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ 876c577b8a1SJoseph Chan 877c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 878c577b8a1SJoseph Chan "Headphone Playback Volume", 879c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 880c577b8a1SJoseph Chan if (err < 0) 881c577b8a1SJoseph Chan return err; 882c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 883c577b8a1SJoseph Chan "Headphone Playback Switch", 884c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 885c577b8a1SJoseph Chan if (err < 0) 886c577b8a1SJoseph Chan return err; 887c577b8a1SJoseph Chan 888c577b8a1SJoseph Chan return 0; 889c577b8a1SJoseph Chan } 890c577b8a1SJoseph Chan 891c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 892c577b8a1SJoseph Chan static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec, 893c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 894c577b8a1SJoseph Chan { 895c577b8a1SJoseph Chan static char *labels[] = { 896c577b8a1SJoseph Chan "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 897c577b8a1SJoseph Chan }; 898c577b8a1SJoseph Chan struct hda_input_mux *imux = &spec->private_imux; 899c577b8a1SJoseph Chan int i, err, idx = 0; 900c577b8a1SJoseph Chan 901c577b8a1SJoseph Chan /* for internal loopback recording select */ 902c577b8a1SJoseph Chan imux->items[imux->num_items].label = "Stereo Mixer"; 903c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 904c577b8a1SJoseph Chan imux->num_items++; 905c577b8a1SJoseph Chan 906c577b8a1SJoseph Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 907c577b8a1SJoseph Chan if (!cfg->input_pins[i]) 908c577b8a1SJoseph Chan continue; 909c577b8a1SJoseph Chan 910c577b8a1SJoseph Chan switch (cfg->input_pins[i]) { 911c577b8a1SJoseph Chan case 0x1d: /* Mic */ 912c577b8a1SJoseph Chan idx = 2; 913c577b8a1SJoseph Chan break; 914c577b8a1SJoseph Chan 915c577b8a1SJoseph Chan case 0x1e: /* Line In */ 916c577b8a1SJoseph Chan idx = 3; 917c577b8a1SJoseph Chan break; 918c577b8a1SJoseph Chan 919c577b8a1SJoseph Chan case 0x21: /* Front Mic */ 920c577b8a1SJoseph Chan idx = 4; 921c577b8a1SJoseph Chan break; 922c577b8a1SJoseph Chan 923c577b8a1SJoseph Chan case 0x24: /* CD */ 924c577b8a1SJoseph Chan idx = 1; 925c577b8a1SJoseph Chan break; 926c577b8a1SJoseph Chan } 927c577b8a1SJoseph Chan err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], 928c577b8a1SJoseph Chan idx, 0x17); 929c577b8a1SJoseph Chan if (err < 0) 930c577b8a1SJoseph Chan return err; 931c577b8a1SJoseph Chan imux->items[imux->num_items].label = labels[i]; 932c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 933c577b8a1SJoseph Chan imux->num_items++; 934c577b8a1SJoseph Chan } 935c577b8a1SJoseph Chan return 0; 936c577b8a1SJoseph Chan } 937c577b8a1SJoseph Chan 938cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 939cb53c626STakashi Iwai static struct hda_amp_list vt1708_loopbacks[] = { 940cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 941cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 942cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 943cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 944cb53c626STakashi Iwai { } /* end */ 945cb53c626STakashi Iwai }; 946cb53c626STakashi Iwai #endif 947cb53c626STakashi Iwai 94876d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 94976d9b0ddSHarald Welte { 95076d9b0ddSHarald Welte unsigned int def_conf; 95176d9b0ddSHarald Welte unsigned char seqassoc; 95276d9b0ddSHarald Welte 95376d9b0ddSHarald Welte def_conf = snd_hda_codec_read(codec, nid, 0, 95476d9b0ddSHarald Welte AC_VERB_GET_CONFIG_DEFAULT, 0); 95576d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 95676d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 95776d9b0ddSHarald Welte if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) { 95876d9b0ddSHarald Welte if (seqassoc == 0xff) { 95976d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 96076d9b0ddSHarald Welte snd_hda_codec_write(codec, nid, 0, 96176d9b0ddSHarald Welte AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 96276d9b0ddSHarald Welte def_conf >> 24); 96376d9b0ddSHarald Welte } 96476d9b0ddSHarald Welte } 96576d9b0ddSHarald Welte 96676d9b0ddSHarald Welte return; 96776d9b0ddSHarald Welte } 96876d9b0ddSHarald Welte 969c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec) 970c577b8a1SJoseph Chan { 971c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 972c577b8a1SJoseph Chan int err; 973c577b8a1SJoseph Chan 97476d9b0ddSHarald Welte /* Add HP and CD pin config connect bit re-config action */ 97576d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 97676d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 97776d9b0ddSHarald Welte 978c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 979c577b8a1SJoseph Chan if (err < 0) 980c577b8a1SJoseph Chan return err; 981c577b8a1SJoseph Chan err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg); 982c577b8a1SJoseph Chan if (err < 0) 983c577b8a1SJoseph Chan return err; 984c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 985c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 986c577b8a1SJoseph Chan 987c577b8a1SJoseph Chan err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg); 988c577b8a1SJoseph Chan if (err < 0) 989c577b8a1SJoseph Chan return err; 990c577b8a1SJoseph Chan err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 991c577b8a1SJoseph Chan if (err < 0) 992c577b8a1SJoseph Chan return err; 993c577b8a1SJoseph Chan err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg); 994c577b8a1SJoseph Chan if (err < 0) 995c577b8a1SJoseph Chan return err; 996c577b8a1SJoseph Chan 997c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 998c577b8a1SJoseph Chan 999c577b8a1SJoseph Chan if (spec->autocfg.dig_out_pin) 1000c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; 1001c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 1002c577b8a1SJoseph Chan spec->dig_in_nid = VT1708_DIGIN_NID; 1003c577b8a1SJoseph Chan 1004c577b8a1SJoseph Chan if (spec->kctl_alloc) 1005c577b8a1SJoseph Chan spec->mixers[spec->num_mixers++] = spec->kctl_alloc; 1006c577b8a1SJoseph Chan 1007*69e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; 1008c577b8a1SJoseph Chan 1009c577b8a1SJoseph Chan spec->input_mux = &spec->private_imux; 1010c577b8a1SJoseph Chan 1011c577b8a1SJoseph Chan return 1; 1012c577b8a1SJoseph Chan } 1013c577b8a1SJoseph Chan 1014c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */ 1015c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec) 1016c577b8a1SJoseph Chan { 1017c577b8a1SJoseph Chan via_init(codec); 1018c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 1019c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 1020c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 1021c577b8a1SJoseph Chan return 0; 1022c577b8a1SJoseph Chan } 1023c577b8a1SJoseph Chan 1024c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 1025c577b8a1SJoseph Chan { 1026c577b8a1SJoseph Chan struct via_spec *spec; 1027c577b8a1SJoseph Chan int err; 1028c577b8a1SJoseph Chan 1029c577b8a1SJoseph Chan /* create a codec specific record */ 1030eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1031c577b8a1SJoseph Chan if (spec == NULL) 1032c577b8a1SJoseph Chan return -ENOMEM; 1033c577b8a1SJoseph Chan 1034c577b8a1SJoseph Chan codec->spec = spec; 1035c577b8a1SJoseph Chan 1036c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 1037c577b8a1SJoseph Chan err = vt1708_parse_auto_config(codec); 1038c577b8a1SJoseph Chan if (err < 0) { 1039c577b8a1SJoseph Chan via_free(codec); 1040c577b8a1SJoseph Chan return err; 1041c577b8a1SJoseph Chan } else if (!err) { 1042c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 1043c577b8a1SJoseph Chan "from BIOS. Using genenic mode...\n"); 1044c577b8a1SJoseph Chan } 1045c577b8a1SJoseph Chan 1046c577b8a1SJoseph Chan 1047c577b8a1SJoseph Chan spec->stream_name_analog = "VT1708 Analog"; 1048c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1708_pcm_analog_playback; 1049bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 1050bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 1051bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 1052c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1708_pcm_analog_capture; 1053c577b8a1SJoseph Chan 1054c577b8a1SJoseph Chan spec->stream_name_digital = "VT1708 Digital"; 1055c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1708_pcm_digital_playback; 1056c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1708_pcm_digital_capture; 1057c577b8a1SJoseph Chan 1058c577b8a1SJoseph Chan 1059c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 1060c577b8a1SJoseph Chan spec->adc_nids = vt1708_adc_nids; 1061c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); 1062c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1708_capture_mixer; 1063c577b8a1SJoseph Chan spec->num_mixers++; 1064c577b8a1SJoseph Chan } 1065c577b8a1SJoseph Chan 1066c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 1067c577b8a1SJoseph Chan 1068c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 1069cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1070cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 1071cb53c626STakashi Iwai #endif 1072c577b8a1SJoseph Chan 1073c577b8a1SJoseph Chan return 0; 1074c577b8a1SJoseph Chan } 1075c577b8a1SJoseph Chan 1076c577b8a1SJoseph Chan /* capture mixer elements */ 1077c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1709_capture_mixer[] = { 1078c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), 1079c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), 1080c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), 1081c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), 1082c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), 1083c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), 1084c577b8a1SJoseph Chan { 1085c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1086c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 1087c577b8a1SJoseph Chan * So call somewhat different.. 1088c577b8a1SJoseph Chan */ 1089c577b8a1SJoseph Chan /* .name = "Capture Source", */ 1090c577b8a1SJoseph Chan .name = "Input Source", 1091c577b8a1SJoseph Chan .count = 1, 1092c577b8a1SJoseph Chan .info = via_mux_enum_info, 1093c577b8a1SJoseph Chan .get = via_mux_enum_get, 1094c577b8a1SJoseph Chan .put = via_mux_enum_put, 1095c577b8a1SJoseph Chan }, 1096c577b8a1SJoseph Chan { } /* end */ 1097c577b8a1SJoseph Chan }; 1098c577b8a1SJoseph Chan 1099*69e52a80SHarald Welte static struct hda_verb vt1709_uniwill_init_verbs[] = { 1100*69e52a80SHarald Welte {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, 1101*69e52a80SHarald Welte { } 1102*69e52a80SHarald Welte }; 1103*69e52a80SHarald Welte 1104c577b8a1SJoseph Chan /* 1105c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1106c577b8a1SJoseph Chan */ 1107c577b8a1SJoseph Chan static struct hda_verb vt1709_10ch_volume_init_verbs[] = { 1108c577b8a1SJoseph Chan /* 1109c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 1110c577b8a1SJoseph Chan */ 1111c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1112c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1113c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1114c577b8a1SJoseph Chan 1115c577b8a1SJoseph Chan 1116f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1117c577b8a1SJoseph Chan * mixer widget 1118c577b8a1SJoseph Chan */ 1119c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1120f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1121f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1122f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1123f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1124f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1125c577b8a1SJoseph Chan 1126c577b8a1SJoseph Chan /* 1127c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 1128c577b8a1SJoseph Chan */ 1129c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 1130c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1131c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1132c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1133c577b8a1SJoseph Chan 1134c577b8a1SJoseph Chan /* 1135c577b8a1SJoseph Chan * Unmute PW3 and PW4 1136c577b8a1SJoseph Chan */ 1137c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1138c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1139c577b8a1SJoseph Chan 1140c577b8a1SJoseph Chan /* Set input of PW4 as AOW4 */ 1141c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, 1142c577b8a1SJoseph Chan /* PW9 Output enable */ 1143c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1144c577b8a1SJoseph Chan { } 1145c577b8a1SJoseph Chan }; 1146c577b8a1SJoseph Chan 1147c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { 1148c577b8a1SJoseph Chan .substreams = 1, 1149c577b8a1SJoseph Chan .channels_min = 2, 1150c577b8a1SJoseph Chan .channels_max = 10, 1151c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 1152c577b8a1SJoseph Chan .ops = { 1153c577b8a1SJoseph Chan .open = via_playback_pcm_open, 1154c577b8a1SJoseph Chan .prepare = via_playback_pcm_prepare, 1155c577b8a1SJoseph Chan .cleanup = via_playback_pcm_cleanup 1156c577b8a1SJoseph Chan }, 1157c577b8a1SJoseph Chan }; 1158c577b8a1SJoseph Chan 1159c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { 1160c577b8a1SJoseph Chan .substreams = 1, 1161c577b8a1SJoseph Chan .channels_min = 2, 1162c577b8a1SJoseph Chan .channels_max = 6, 1163c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 1164c577b8a1SJoseph Chan .ops = { 1165c577b8a1SJoseph Chan .open = via_playback_pcm_open, 1166c577b8a1SJoseph Chan .prepare = via_playback_pcm_prepare, 1167c577b8a1SJoseph Chan .cleanup = via_playback_pcm_cleanup 1168c577b8a1SJoseph Chan }, 1169c577b8a1SJoseph Chan }; 1170c577b8a1SJoseph Chan 1171c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_analog_capture = { 1172c577b8a1SJoseph Chan .substreams = 2, 1173c577b8a1SJoseph Chan .channels_min = 2, 1174c577b8a1SJoseph Chan .channels_max = 2, 1175c577b8a1SJoseph Chan .nid = 0x14, /* NID to query formats and rates */ 1176c577b8a1SJoseph Chan .ops = { 1177c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1178c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1179c577b8a1SJoseph Chan }, 1180c577b8a1SJoseph Chan }; 1181c577b8a1SJoseph Chan 1182c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_playback = { 1183c577b8a1SJoseph Chan .substreams = 1, 1184c577b8a1SJoseph Chan .channels_min = 2, 1185c577b8a1SJoseph Chan .channels_max = 2, 1186c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1187c577b8a1SJoseph Chan .ops = { 1188c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 1189c577b8a1SJoseph Chan .close = via_dig_playback_pcm_close 1190c577b8a1SJoseph Chan }, 1191c577b8a1SJoseph Chan }; 1192c577b8a1SJoseph Chan 1193c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_capture = { 1194c577b8a1SJoseph Chan .substreams = 1, 1195c577b8a1SJoseph Chan .channels_min = 2, 1196c577b8a1SJoseph Chan .channels_max = 2, 1197c577b8a1SJoseph Chan }; 1198c577b8a1SJoseph Chan 1199c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec, 1200c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1201c577b8a1SJoseph Chan { 1202c577b8a1SJoseph Chan int i; 1203c577b8a1SJoseph Chan hda_nid_t nid; 1204c577b8a1SJoseph Chan 1205c577b8a1SJoseph Chan if (cfg->line_outs == 4) /* 10 channels */ 1206c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */ 1207c577b8a1SJoseph Chan else if (cfg->line_outs == 3) /* 6 channels */ 1208c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */ 1209c577b8a1SJoseph Chan 1210c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 1211c577b8a1SJoseph Chan 1212c577b8a1SJoseph Chan if (cfg->line_outs == 4) { /* 10 channels */ 1213c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 1214c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1215c577b8a1SJoseph Chan if (nid) { 1216c577b8a1SJoseph Chan /* config dac list */ 1217c577b8a1SJoseph Chan switch (i) { 1218c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 1219c577b8a1SJoseph Chan /* AOW0 */ 1220c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 1221c577b8a1SJoseph Chan break; 1222c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 1223c577b8a1SJoseph Chan /* AOW2 */ 1224c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 1225c577b8a1SJoseph Chan break; 1226c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 1227c577b8a1SJoseph Chan /* AOW3 */ 1228fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 1229c577b8a1SJoseph Chan break; 1230c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 1231c577b8a1SJoseph Chan /* AOW1 */ 1232fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x27; 1233c577b8a1SJoseph Chan break; 1234c577b8a1SJoseph Chan default: 1235c577b8a1SJoseph Chan break; 1236c577b8a1SJoseph Chan } 1237c577b8a1SJoseph Chan } 1238c577b8a1SJoseph Chan } 1239c577b8a1SJoseph Chan spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ 1240c577b8a1SJoseph Chan 1241c577b8a1SJoseph Chan } else if (cfg->line_outs == 3) { /* 6 channels */ 1242c577b8a1SJoseph Chan for(i = 0; i < cfg->line_outs; i++) { 1243c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1244c577b8a1SJoseph Chan if (nid) { 1245c577b8a1SJoseph Chan /* config dac list */ 1246c577b8a1SJoseph Chan switch(i) { 1247c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 1248c577b8a1SJoseph Chan /* AOW0 */ 1249c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 1250c577b8a1SJoseph Chan break; 1251c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 1252c577b8a1SJoseph Chan /* AOW2 */ 1253c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 1254c577b8a1SJoseph Chan break; 1255c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 1256c577b8a1SJoseph Chan /* AOW1 */ 1257c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x11; 1258c577b8a1SJoseph Chan break; 1259c577b8a1SJoseph Chan default: 1260c577b8a1SJoseph Chan break; 1261c577b8a1SJoseph Chan } 1262c577b8a1SJoseph Chan } 1263c577b8a1SJoseph Chan } 1264c577b8a1SJoseph Chan } 1265c577b8a1SJoseph Chan 1266c577b8a1SJoseph Chan return 0; 1267c577b8a1SJoseph Chan } 1268c577b8a1SJoseph Chan 1269c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 1270c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, 1271c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1272c577b8a1SJoseph Chan { 1273c577b8a1SJoseph Chan char name[32]; 1274c577b8a1SJoseph Chan static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 1275c577b8a1SJoseph Chan hda_nid_t nid = 0; 1276c577b8a1SJoseph Chan int i, err; 1277c577b8a1SJoseph Chan 1278c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 1279c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1280c577b8a1SJoseph Chan 1281c577b8a1SJoseph Chan if (!nid) 1282c577b8a1SJoseph Chan continue; 1283c577b8a1SJoseph Chan 1284c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 1285c577b8a1SJoseph Chan /* Center/LFE */ 1286c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1287c577b8a1SJoseph Chan "Center Playback Volume", 1288f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, 1289f7278fd0SJosepch Chan HDA_OUTPUT)); 1290c577b8a1SJoseph Chan if (err < 0) 1291c577b8a1SJoseph Chan return err; 1292c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1293c577b8a1SJoseph Chan "LFE Playback Volume", 1294f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, 1295f7278fd0SJosepch Chan HDA_OUTPUT)); 1296c577b8a1SJoseph Chan if (err < 0) 1297c577b8a1SJoseph Chan return err; 1298c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1299c577b8a1SJoseph Chan "Center Playback Switch", 1300f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, 1301f7278fd0SJosepch Chan HDA_OUTPUT)); 1302c577b8a1SJoseph Chan if (err < 0) 1303c577b8a1SJoseph Chan return err; 1304c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1305c577b8a1SJoseph Chan "LFE Playback Switch", 1306f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, 1307f7278fd0SJosepch Chan HDA_OUTPUT)); 1308c577b8a1SJoseph Chan if (err < 0) 1309c577b8a1SJoseph Chan return err; 1310c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT){ 1311c577b8a1SJoseph Chan /* add control to mixer index 0 */ 1312c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1313c577b8a1SJoseph Chan "Master Front Playback Volume", 1314f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x18, 3, 0, 1315f7278fd0SJosepch Chan HDA_INPUT)); 1316c577b8a1SJoseph Chan if (err < 0) 1317c577b8a1SJoseph Chan return err; 1318c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1319c577b8a1SJoseph Chan "Master Front Playback Switch", 1320f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(0x18, 3, 0, 1321f7278fd0SJosepch Chan HDA_INPUT)); 1322c577b8a1SJoseph Chan if (err < 0) 1323c577b8a1SJoseph Chan return err; 1324c577b8a1SJoseph Chan 1325c577b8a1SJoseph Chan /* add control to PW3 */ 1326c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1327c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1328f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1329f7278fd0SJosepch Chan HDA_OUTPUT)); 1330c577b8a1SJoseph Chan if (err < 0) 1331c577b8a1SJoseph Chan return err; 1332c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 1333c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1334f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1335f7278fd0SJosepch Chan HDA_OUTPUT)); 1336c577b8a1SJoseph Chan if (err < 0) 1337c577b8a1SJoseph Chan return err; 1338c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SURROUND) { 1339c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1340c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1341fb4cb772SHarald Welte HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, 1342f7278fd0SJosepch Chan HDA_OUTPUT)); 1343c577b8a1SJoseph Chan if (err < 0) 1344c577b8a1SJoseph Chan return err; 1345c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 1346c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1347fb4cb772SHarald Welte HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, 1348f7278fd0SJosepch Chan HDA_OUTPUT)); 1349c577b8a1SJoseph Chan if (err < 0) 1350c577b8a1SJoseph Chan return err; 1351c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SIDE) { 1352c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1353c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1354fb4cb772SHarald Welte HDA_COMPOSE_AMP_VAL(0x29, 3, 0, 1355f7278fd0SJosepch Chan HDA_OUTPUT)); 1356c577b8a1SJoseph Chan if (err < 0) 1357c577b8a1SJoseph Chan return err; 1358c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 1359c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1360fb4cb772SHarald Welte HDA_COMPOSE_AMP_VAL(0x29, 3, 0, 1361f7278fd0SJosepch Chan HDA_OUTPUT)); 1362c577b8a1SJoseph Chan if (err < 0) 1363c577b8a1SJoseph Chan return err; 1364c577b8a1SJoseph Chan } 1365c577b8a1SJoseph Chan } 1366c577b8a1SJoseph Chan 1367c577b8a1SJoseph Chan return 0; 1368c577b8a1SJoseph Chan } 1369c577b8a1SJoseph Chan 1370c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 1371c577b8a1SJoseph Chan { 1372c577b8a1SJoseph Chan int err; 1373c577b8a1SJoseph Chan 1374c577b8a1SJoseph Chan if (!pin) 1375c577b8a1SJoseph Chan return 0; 1376c577b8a1SJoseph Chan 1377c577b8a1SJoseph Chan if (spec->multiout.num_dacs == 5) /* 10 channels */ 1378c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1709_HP_DAC_NID; 1379c577b8a1SJoseph Chan else if (spec->multiout.num_dacs == 3) /* 6 channels */ 1380c577b8a1SJoseph Chan spec->multiout.hp_nid = 0; 1381c577b8a1SJoseph Chan 1382c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1383c577b8a1SJoseph Chan "Headphone Playback Volume", 1384c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 1385c577b8a1SJoseph Chan if (err < 0) 1386c577b8a1SJoseph Chan return err; 1387c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1388c577b8a1SJoseph Chan "Headphone Playback Switch", 1389c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 1390c577b8a1SJoseph Chan if (err < 0) 1391c577b8a1SJoseph Chan return err; 1392c577b8a1SJoseph Chan 1393c577b8a1SJoseph Chan return 0; 1394c577b8a1SJoseph Chan } 1395c577b8a1SJoseph Chan 1396c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 1397c577b8a1SJoseph Chan static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec, 1398c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1399c577b8a1SJoseph Chan { 1400c577b8a1SJoseph Chan static char *labels[] = { 1401c577b8a1SJoseph Chan "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 1402c577b8a1SJoseph Chan }; 1403c577b8a1SJoseph Chan struct hda_input_mux *imux = &spec->private_imux; 1404c577b8a1SJoseph Chan int i, err, idx = 0; 1405c577b8a1SJoseph Chan 1406c577b8a1SJoseph Chan /* for internal loopback recording select */ 1407c577b8a1SJoseph Chan imux->items[imux->num_items].label = "Stereo Mixer"; 1408c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 1409c577b8a1SJoseph Chan imux->num_items++; 1410c577b8a1SJoseph Chan 1411c577b8a1SJoseph Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 1412c577b8a1SJoseph Chan if (!cfg->input_pins[i]) 1413c577b8a1SJoseph Chan continue; 1414c577b8a1SJoseph Chan 1415c577b8a1SJoseph Chan switch (cfg->input_pins[i]) { 1416c577b8a1SJoseph Chan case 0x1d: /* Mic */ 1417c577b8a1SJoseph Chan idx = 2; 1418c577b8a1SJoseph Chan break; 1419c577b8a1SJoseph Chan 1420c577b8a1SJoseph Chan case 0x1e: /* Line In */ 1421c577b8a1SJoseph Chan idx = 3; 1422c577b8a1SJoseph Chan break; 1423c577b8a1SJoseph Chan 1424c577b8a1SJoseph Chan case 0x21: /* Front Mic */ 1425c577b8a1SJoseph Chan idx = 4; 1426c577b8a1SJoseph Chan break; 1427c577b8a1SJoseph Chan 1428c577b8a1SJoseph Chan case 0x23: /* CD */ 1429c577b8a1SJoseph Chan idx = 1; 1430c577b8a1SJoseph Chan break; 1431c577b8a1SJoseph Chan } 1432c577b8a1SJoseph Chan err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], 1433c577b8a1SJoseph Chan idx, 0x18); 1434c577b8a1SJoseph Chan if (err < 0) 1435c577b8a1SJoseph Chan return err; 1436c577b8a1SJoseph Chan imux->items[imux->num_items].label = labels[i]; 1437c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 1438c577b8a1SJoseph Chan imux->num_items++; 1439c577b8a1SJoseph Chan } 1440c577b8a1SJoseph Chan return 0; 1441c577b8a1SJoseph Chan } 1442c577b8a1SJoseph Chan 1443c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec) 1444c577b8a1SJoseph Chan { 1445c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1446c577b8a1SJoseph Chan int err; 1447c577b8a1SJoseph Chan 1448c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 1449c577b8a1SJoseph Chan if (err < 0) 1450c577b8a1SJoseph Chan return err; 1451c577b8a1SJoseph Chan err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg); 1452c577b8a1SJoseph Chan if (err < 0) 1453c577b8a1SJoseph Chan return err; 1454c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 1455c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 1456c577b8a1SJoseph Chan 1457c577b8a1SJoseph Chan err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg); 1458c577b8a1SJoseph Chan if (err < 0) 1459c577b8a1SJoseph Chan return err; 1460c577b8a1SJoseph Chan err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 1461c577b8a1SJoseph Chan if (err < 0) 1462c577b8a1SJoseph Chan return err; 1463c577b8a1SJoseph Chan err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg); 1464c577b8a1SJoseph Chan if (err < 0) 1465c577b8a1SJoseph Chan return err; 1466c577b8a1SJoseph Chan 1467c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 1468c577b8a1SJoseph Chan 1469c577b8a1SJoseph Chan if (spec->autocfg.dig_out_pin) 1470c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; 1471c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 1472c577b8a1SJoseph Chan spec->dig_in_nid = VT1709_DIGIN_NID; 1473c577b8a1SJoseph Chan 1474c577b8a1SJoseph Chan if (spec->kctl_alloc) 1475c577b8a1SJoseph Chan spec->mixers[spec->num_mixers++] = spec->kctl_alloc; 1476c577b8a1SJoseph Chan 1477c577b8a1SJoseph Chan spec->input_mux = &spec->private_imux; 1478c577b8a1SJoseph Chan 1479c577b8a1SJoseph Chan return 1; 1480c577b8a1SJoseph Chan } 1481c577b8a1SJoseph Chan 1482cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1483cb53c626STakashi Iwai static struct hda_amp_list vt1709_loopbacks[] = { 1484cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 1485cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 1486cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 1487cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 1488cb53c626STakashi Iwai { } /* end */ 1489cb53c626STakashi Iwai }; 1490cb53c626STakashi Iwai #endif 1491cb53c626STakashi Iwai 1492c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 1493c577b8a1SJoseph Chan { 1494c577b8a1SJoseph Chan struct via_spec *spec; 1495c577b8a1SJoseph Chan int err; 1496c577b8a1SJoseph Chan 1497c577b8a1SJoseph Chan /* create a codec specific record */ 1498eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1499c577b8a1SJoseph Chan if (spec == NULL) 1500c577b8a1SJoseph Chan return -ENOMEM; 1501c577b8a1SJoseph Chan 1502c577b8a1SJoseph Chan codec->spec = spec; 1503c577b8a1SJoseph Chan 1504c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 1505c577b8a1SJoseph Chan if (err < 0) { 1506c577b8a1SJoseph Chan via_free(codec); 1507c577b8a1SJoseph Chan return err; 1508c577b8a1SJoseph Chan } else if (!err) { 1509c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 1510c577b8a1SJoseph Chan "Using genenic mode...\n"); 1511c577b8a1SJoseph Chan } 1512c577b8a1SJoseph Chan 1513*69e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; 1514*69e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 1515c577b8a1SJoseph Chan 1516c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 1517c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; 1518c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 1519c577b8a1SJoseph Chan 1520c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 1521c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 1522c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 1523c577b8a1SJoseph Chan 1524c577b8a1SJoseph Chan 1525c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 1526c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 1527c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 1528c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 1529c577b8a1SJoseph Chan spec->num_mixers++; 1530c577b8a1SJoseph Chan } 1531c577b8a1SJoseph Chan 1532c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 1533c577b8a1SJoseph Chan 1534c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 1535*69e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 1536cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1537cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 1538cb53c626STakashi Iwai #endif 1539c577b8a1SJoseph Chan 1540c577b8a1SJoseph Chan return 0; 1541c577b8a1SJoseph Chan } 1542c577b8a1SJoseph Chan /* 1543c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1544c577b8a1SJoseph Chan */ 1545c577b8a1SJoseph Chan static struct hda_verb vt1709_6ch_volume_init_verbs[] = { 1546c577b8a1SJoseph Chan /* 1547c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 1548c577b8a1SJoseph Chan */ 1549c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1550c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1551c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1552c577b8a1SJoseph Chan 1553c577b8a1SJoseph Chan 1554c577b8a1SJoseph Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1555c577b8a1SJoseph Chan * mixer widget 1556c577b8a1SJoseph Chan */ 1557c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1558c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1559c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1560c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1561c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1562c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1563c577b8a1SJoseph Chan 1564c577b8a1SJoseph Chan /* 1565c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 1566c577b8a1SJoseph Chan */ 1567c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 1568c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1569c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1570c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1571c577b8a1SJoseph Chan 1572c577b8a1SJoseph Chan /* 1573c577b8a1SJoseph Chan * Unmute PW3 and PW4 1574c577b8a1SJoseph Chan */ 1575c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1576c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1577c577b8a1SJoseph Chan 1578c577b8a1SJoseph Chan /* Set input of PW4 as MW0 */ 1579c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 1580c577b8a1SJoseph Chan /* PW9 Output enable */ 1581c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1582c577b8a1SJoseph Chan { } 1583c577b8a1SJoseph Chan }; 1584c577b8a1SJoseph Chan 1585c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 1586c577b8a1SJoseph Chan { 1587c577b8a1SJoseph Chan struct via_spec *spec; 1588c577b8a1SJoseph Chan int err; 1589c577b8a1SJoseph Chan 1590c577b8a1SJoseph Chan /* create a codec specific record */ 1591eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1592c577b8a1SJoseph Chan if (spec == NULL) 1593c577b8a1SJoseph Chan return -ENOMEM; 1594c577b8a1SJoseph Chan 1595c577b8a1SJoseph Chan codec->spec = spec; 1596c577b8a1SJoseph Chan 1597c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 1598c577b8a1SJoseph Chan if (err < 0) { 1599c577b8a1SJoseph Chan via_free(codec); 1600c577b8a1SJoseph Chan return err; 1601c577b8a1SJoseph Chan } else if (!err) { 1602c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 1603c577b8a1SJoseph Chan "Using genenic mode...\n"); 1604c577b8a1SJoseph Chan } 1605c577b8a1SJoseph Chan 1606*69e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; 1607*69e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 1608c577b8a1SJoseph Chan 1609c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 1610c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; 1611c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 1612c577b8a1SJoseph Chan 1613c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 1614c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 1615c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 1616c577b8a1SJoseph Chan 1617c577b8a1SJoseph Chan 1618c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 1619c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 1620c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 1621c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 1622c577b8a1SJoseph Chan spec->num_mixers++; 1623c577b8a1SJoseph Chan } 1624c577b8a1SJoseph Chan 1625c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 1626c577b8a1SJoseph Chan 1627c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 1628*69e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 1629cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1630cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 1631cb53c626STakashi Iwai #endif 1632f7278fd0SJosepch Chan return 0; 1633f7278fd0SJosepch Chan } 1634f7278fd0SJosepch Chan 1635f7278fd0SJosepch Chan /* capture mixer elements */ 1636f7278fd0SJosepch Chan static struct snd_kcontrol_new vt1708B_capture_mixer[] = { 1637f7278fd0SJosepch Chan HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 1638f7278fd0SJosepch Chan HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 1639f7278fd0SJosepch Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 1640f7278fd0SJosepch Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 1641f7278fd0SJosepch Chan { 1642f7278fd0SJosepch Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1643f7278fd0SJosepch Chan /* The multiple "Capture Source" controls confuse alsamixer 1644f7278fd0SJosepch Chan * So call somewhat different.. 1645f7278fd0SJosepch Chan */ 1646f7278fd0SJosepch Chan /* .name = "Capture Source", */ 1647f7278fd0SJosepch Chan .name = "Input Source", 1648f7278fd0SJosepch Chan .count = 1, 1649f7278fd0SJosepch Chan .info = via_mux_enum_info, 1650f7278fd0SJosepch Chan .get = via_mux_enum_get, 1651f7278fd0SJosepch Chan .put = via_mux_enum_put, 1652f7278fd0SJosepch Chan }, 1653f7278fd0SJosepch Chan { } /* end */ 1654f7278fd0SJosepch Chan }; 1655f7278fd0SJosepch Chan /* 1656f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 1657f7278fd0SJosepch Chan */ 1658f7278fd0SJosepch Chan static struct hda_verb vt1708B_8ch_volume_init_verbs[] = { 1659f7278fd0SJosepch Chan /* 1660f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 1661f7278fd0SJosepch Chan */ 1662f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1663f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1664f7278fd0SJosepch Chan 1665f7278fd0SJosepch Chan 1666f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1667f7278fd0SJosepch Chan * mixer widget 1668f7278fd0SJosepch Chan */ 1669f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1670f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1671f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1672f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1673f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1674f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1675f7278fd0SJosepch Chan 1676f7278fd0SJosepch Chan /* 1677f7278fd0SJosepch Chan * Set up output mixers 1678f7278fd0SJosepch Chan */ 1679f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 1680f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1681f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1682f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1683f7278fd0SJosepch Chan 1684f7278fd0SJosepch Chan /* Setup default input to PW4 */ 1685f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, 1686f7278fd0SJosepch Chan /* PW9 Output enable */ 1687f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1688f7278fd0SJosepch Chan /* PW10 Input enable */ 1689f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 1690f7278fd0SJosepch Chan { } 1691f7278fd0SJosepch Chan }; 1692f7278fd0SJosepch Chan 1693f7278fd0SJosepch Chan static struct hda_verb vt1708B_4ch_volume_init_verbs[] = { 1694f7278fd0SJosepch Chan /* 1695f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 1696f7278fd0SJosepch Chan */ 1697f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1698f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1699f7278fd0SJosepch Chan 1700f7278fd0SJosepch Chan 1701f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1702f7278fd0SJosepch Chan * mixer widget 1703f7278fd0SJosepch Chan */ 1704f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1705f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1706f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1707f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1708f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1709f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1710f7278fd0SJosepch Chan 1711f7278fd0SJosepch Chan /* 1712f7278fd0SJosepch Chan * Set up output mixers 1713f7278fd0SJosepch Chan */ 1714f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 1715f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1716f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1717f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1718f7278fd0SJosepch Chan 1719f7278fd0SJosepch Chan /* Setup default input of PW4 to MW0 */ 1720f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 1721f7278fd0SJosepch Chan /* PW9 Output enable */ 1722f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1723f7278fd0SJosepch Chan /* PW10 Input enable */ 1724f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 1725f7278fd0SJosepch Chan { } 1726f7278fd0SJosepch Chan }; 1727f7278fd0SJosepch Chan 1728*69e52a80SHarald Welte static struct hda_verb vt1708B_uniwill_init_verbs[] = { 1729*69e52a80SHarald Welte {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, 1730*69e52a80SHarald Welte { } 1731*69e52a80SHarald Welte }; 1732*69e52a80SHarald Welte 1733f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { 1734f7278fd0SJosepch Chan .substreams = 1, 1735f7278fd0SJosepch Chan .channels_min = 2, 1736f7278fd0SJosepch Chan .channels_max = 8, 1737f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 1738f7278fd0SJosepch Chan .ops = { 1739f7278fd0SJosepch Chan .open = via_playback_pcm_open, 1740f7278fd0SJosepch Chan .prepare = via_playback_pcm_prepare, 1741f7278fd0SJosepch Chan .cleanup = via_playback_pcm_cleanup 1742f7278fd0SJosepch Chan }, 1743f7278fd0SJosepch Chan }; 1744f7278fd0SJosepch Chan 1745f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { 1746f7278fd0SJosepch Chan .substreams = 1, 1747f7278fd0SJosepch Chan .channels_min = 2, 1748f7278fd0SJosepch Chan .channels_max = 4, 1749f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 1750f7278fd0SJosepch Chan .ops = { 1751f7278fd0SJosepch Chan .open = via_playback_pcm_open, 1752f7278fd0SJosepch Chan .prepare = via_playback_pcm_prepare, 1753f7278fd0SJosepch Chan .cleanup = via_playback_pcm_cleanup 1754f7278fd0SJosepch Chan }, 1755f7278fd0SJosepch Chan }; 1756f7278fd0SJosepch Chan 1757f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_analog_capture = { 1758f7278fd0SJosepch Chan .substreams = 2, 1759f7278fd0SJosepch Chan .channels_min = 2, 1760f7278fd0SJosepch Chan .channels_max = 2, 1761f7278fd0SJosepch Chan .nid = 0x13, /* NID to query formats and rates */ 1762f7278fd0SJosepch Chan .ops = { 1763f7278fd0SJosepch Chan .prepare = via_capture_pcm_prepare, 1764f7278fd0SJosepch Chan .cleanup = via_capture_pcm_cleanup 1765f7278fd0SJosepch Chan }, 1766f7278fd0SJosepch Chan }; 1767f7278fd0SJosepch Chan 1768f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_playback = { 1769f7278fd0SJosepch Chan .substreams = 1, 1770f7278fd0SJosepch Chan .channels_min = 2, 1771f7278fd0SJosepch Chan .channels_max = 2, 1772f7278fd0SJosepch Chan /* NID is set in via_build_pcms */ 1773f7278fd0SJosepch Chan .ops = { 1774f7278fd0SJosepch Chan .open = via_dig_playback_pcm_open, 1775f7278fd0SJosepch Chan .close = via_dig_playback_pcm_close, 1776f7278fd0SJosepch Chan .prepare = via_dig_playback_pcm_prepare 1777f7278fd0SJosepch Chan }, 1778f7278fd0SJosepch Chan }; 1779f7278fd0SJosepch Chan 1780f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_capture = { 1781f7278fd0SJosepch Chan .substreams = 1, 1782f7278fd0SJosepch Chan .channels_min = 2, 1783f7278fd0SJosepch Chan .channels_max = 2, 1784f7278fd0SJosepch Chan }; 1785f7278fd0SJosepch Chan 1786f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */ 1787f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, 1788f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 1789f7278fd0SJosepch Chan { 1790f7278fd0SJosepch Chan int i; 1791f7278fd0SJosepch Chan hda_nid_t nid; 1792f7278fd0SJosepch Chan 1793f7278fd0SJosepch Chan spec->multiout.num_dacs = cfg->line_outs; 1794f7278fd0SJosepch Chan 1795f7278fd0SJosepch Chan spec->multiout.dac_nids = spec->private_dac_nids; 1796f7278fd0SJosepch Chan 1797f7278fd0SJosepch Chan for (i = 0; i < 4; i++) { 1798f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 1799f7278fd0SJosepch Chan if (nid) { 1800f7278fd0SJosepch Chan /* config dac list */ 1801f7278fd0SJosepch Chan switch (i) { 1802f7278fd0SJosepch Chan case AUTO_SEQ_FRONT: 1803f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x10; 1804f7278fd0SJosepch Chan break; 1805f7278fd0SJosepch Chan case AUTO_SEQ_CENLFE: 1806f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x24; 1807f7278fd0SJosepch Chan break; 1808f7278fd0SJosepch Chan case AUTO_SEQ_SURROUND: 1809fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 1810f7278fd0SJosepch Chan break; 1811f7278fd0SJosepch Chan case AUTO_SEQ_SIDE: 1812fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x25; 1813f7278fd0SJosepch Chan break; 1814f7278fd0SJosepch Chan } 1815f7278fd0SJosepch Chan } 1816f7278fd0SJosepch Chan } 1817f7278fd0SJosepch Chan 1818f7278fd0SJosepch Chan return 0; 1819f7278fd0SJosepch Chan } 1820f7278fd0SJosepch Chan 1821f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */ 1822f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, 1823f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 1824f7278fd0SJosepch Chan { 1825f7278fd0SJosepch Chan char name[32]; 1826f7278fd0SJosepch Chan static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 1827fb4cb772SHarald Welte hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27}; 1828f7278fd0SJosepch Chan hda_nid_t nid, nid_vol = 0; 1829f7278fd0SJosepch Chan int i, err; 1830f7278fd0SJosepch Chan 1831f7278fd0SJosepch Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 1832f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 1833f7278fd0SJosepch Chan 1834f7278fd0SJosepch Chan if (!nid) 1835f7278fd0SJosepch Chan continue; 1836f7278fd0SJosepch Chan 1837f7278fd0SJosepch Chan nid_vol = nid_vols[i]; 1838f7278fd0SJosepch Chan 1839f7278fd0SJosepch Chan if (i == AUTO_SEQ_CENLFE) { 1840f7278fd0SJosepch Chan /* Center/LFE */ 1841f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1842f7278fd0SJosepch Chan "Center Playback Volume", 1843f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 1844f7278fd0SJosepch Chan HDA_OUTPUT)); 1845f7278fd0SJosepch Chan if (err < 0) 1846f7278fd0SJosepch Chan return err; 1847f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1848f7278fd0SJosepch Chan "LFE Playback Volume", 1849f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 1850f7278fd0SJosepch Chan HDA_OUTPUT)); 1851f7278fd0SJosepch Chan if (err < 0) 1852f7278fd0SJosepch Chan return err; 1853f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1854f7278fd0SJosepch Chan "Center Playback Switch", 1855f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 1856f7278fd0SJosepch Chan HDA_OUTPUT)); 1857f7278fd0SJosepch Chan if (err < 0) 1858f7278fd0SJosepch Chan return err; 1859f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1860f7278fd0SJosepch Chan "LFE Playback Switch", 1861f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 1862f7278fd0SJosepch Chan HDA_OUTPUT)); 1863f7278fd0SJosepch Chan if (err < 0) 1864f7278fd0SJosepch Chan return err; 1865f7278fd0SJosepch Chan } else if (i == AUTO_SEQ_FRONT) { 1866f7278fd0SJosepch Chan /* add control to mixer index 0 */ 1867f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1868f7278fd0SJosepch Chan "Master Front Playback Volume", 1869f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1870f7278fd0SJosepch Chan HDA_INPUT)); 1871f7278fd0SJosepch Chan if (err < 0) 1872f7278fd0SJosepch Chan return err; 1873f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1874f7278fd0SJosepch Chan "Master Front Playback Switch", 1875f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1876f7278fd0SJosepch Chan HDA_INPUT)); 1877f7278fd0SJosepch Chan if (err < 0) 1878f7278fd0SJosepch Chan return err; 1879f7278fd0SJosepch Chan 1880f7278fd0SJosepch Chan /* add control to PW3 */ 1881f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 1882f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1883f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1884f7278fd0SJosepch Chan HDA_OUTPUT)); 1885f7278fd0SJosepch Chan if (err < 0) 1886f7278fd0SJosepch Chan return err; 1887f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 1888f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1889f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1890f7278fd0SJosepch Chan HDA_OUTPUT)); 1891f7278fd0SJosepch Chan if (err < 0) 1892f7278fd0SJosepch Chan return err; 1893f7278fd0SJosepch Chan } else { 1894f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 1895f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1896f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1897f7278fd0SJosepch Chan HDA_OUTPUT)); 1898f7278fd0SJosepch Chan if (err < 0) 1899f7278fd0SJosepch Chan return err; 1900f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 1901f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1902f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1903f7278fd0SJosepch Chan HDA_OUTPUT)); 1904f7278fd0SJosepch Chan if (err < 0) 1905f7278fd0SJosepch Chan return err; 1906f7278fd0SJosepch Chan } 1907f7278fd0SJosepch Chan } 1908f7278fd0SJosepch Chan 1909f7278fd0SJosepch Chan return 0; 1910f7278fd0SJosepch Chan } 1911f7278fd0SJosepch Chan 1912f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 1913f7278fd0SJosepch Chan { 1914f7278fd0SJosepch Chan int err; 1915f7278fd0SJosepch Chan 1916f7278fd0SJosepch Chan if (!pin) 1917f7278fd0SJosepch Chan return 0; 1918f7278fd0SJosepch Chan 1919f7278fd0SJosepch Chan spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ 1920f7278fd0SJosepch Chan 1921f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1922f7278fd0SJosepch Chan "Headphone Playback Volume", 1923f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 1924f7278fd0SJosepch Chan if (err < 0) 1925f7278fd0SJosepch Chan return err; 1926f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1927f7278fd0SJosepch Chan "Headphone Playback Switch", 1928f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 1929f7278fd0SJosepch Chan if (err < 0) 1930f7278fd0SJosepch Chan return err; 1931f7278fd0SJosepch Chan 1932f7278fd0SJosepch Chan return 0; 1933f7278fd0SJosepch Chan } 1934f7278fd0SJosepch Chan 1935f7278fd0SJosepch Chan /* create playback/capture controls for input pins */ 1936f7278fd0SJosepch Chan static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec, 1937f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 1938f7278fd0SJosepch Chan { 1939f7278fd0SJosepch Chan static char *labels[] = { 1940f7278fd0SJosepch Chan "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 1941f7278fd0SJosepch Chan }; 1942f7278fd0SJosepch Chan struct hda_input_mux *imux = &spec->private_imux; 1943f7278fd0SJosepch Chan int i, err, idx = 0; 1944f7278fd0SJosepch Chan 1945f7278fd0SJosepch Chan /* for internal loopback recording select */ 1946f7278fd0SJosepch Chan imux->items[imux->num_items].label = "Stereo Mixer"; 1947f7278fd0SJosepch Chan imux->items[imux->num_items].index = idx; 1948f7278fd0SJosepch Chan imux->num_items++; 1949f7278fd0SJosepch Chan 1950f7278fd0SJosepch Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 1951f7278fd0SJosepch Chan if (!cfg->input_pins[i]) 1952f7278fd0SJosepch Chan continue; 1953f7278fd0SJosepch Chan 1954f7278fd0SJosepch Chan switch (cfg->input_pins[i]) { 1955f7278fd0SJosepch Chan case 0x1a: /* Mic */ 1956f7278fd0SJosepch Chan idx = 2; 1957f7278fd0SJosepch Chan break; 1958f7278fd0SJosepch Chan 1959f7278fd0SJosepch Chan case 0x1b: /* Line In */ 1960f7278fd0SJosepch Chan idx = 3; 1961f7278fd0SJosepch Chan break; 1962f7278fd0SJosepch Chan 1963f7278fd0SJosepch Chan case 0x1e: /* Front Mic */ 1964f7278fd0SJosepch Chan idx = 4; 1965f7278fd0SJosepch Chan break; 1966f7278fd0SJosepch Chan 1967f7278fd0SJosepch Chan case 0x1f: /* CD */ 1968f7278fd0SJosepch Chan idx = 1; 1969f7278fd0SJosepch Chan break; 1970f7278fd0SJosepch Chan } 1971f7278fd0SJosepch Chan err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], 1972f7278fd0SJosepch Chan idx, 0x16); 1973f7278fd0SJosepch Chan if (err < 0) 1974f7278fd0SJosepch Chan return err; 1975f7278fd0SJosepch Chan imux->items[imux->num_items].label = labels[i]; 1976f7278fd0SJosepch Chan imux->items[imux->num_items].index = idx; 1977f7278fd0SJosepch Chan imux->num_items++; 1978f7278fd0SJosepch Chan } 1979f7278fd0SJosepch Chan return 0; 1980f7278fd0SJosepch Chan } 1981f7278fd0SJosepch Chan 1982f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec) 1983f7278fd0SJosepch Chan { 1984f7278fd0SJosepch Chan struct via_spec *spec = codec->spec; 1985f7278fd0SJosepch Chan int err; 1986f7278fd0SJosepch Chan 1987f7278fd0SJosepch Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 1988f7278fd0SJosepch Chan if (err < 0) 1989f7278fd0SJosepch Chan return err; 1990f7278fd0SJosepch Chan err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); 1991f7278fd0SJosepch Chan if (err < 0) 1992f7278fd0SJosepch Chan return err; 1993f7278fd0SJosepch Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 1994f7278fd0SJosepch Chan return 0; /* can't find valid BIOS pin config */ 1995f7278fd0SJosepch Chan 1996f7278fd0SJosepch Chan err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); 1997f7278fd0SJosepch Chan if (err < 0) 1998f7278fd0SJosepch Chan return err; 1999f7278fd0SJosepch Chan err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2000f7278fd0SJosepch Chan if (err < 0) 2001f7278fd0SJosepch Chan return err; 2002f7278fd0SJosepch Chan err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg); 2003f7278fd0SJosepch Chan if (err < 0) 2004f7278fd0SJosepch Chan return err; 2005f7278fd0SJosepch Chan 2006f7278fd0SJosepch Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2007f7278fd0SJosepch Chan 2008f7278fd0SJosepch Chan if (spec->autocfg.dig_out_pin) 2009f7278fd0SJosepch Chan spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; 2010f7278fd0SJosepch Chan if (spec->autocfg.dig_in_pin) 2011f7278fd0SJosepch Chan spec->dig_in_nid = VT1708B_DIGIN_NID; 2012f7278fd0SJosepch Chan 2013f7278fd0SJosepch Chan if (spec->kctl_alloc) 2014f7278fd0SJosepch Chan spec->mixers[spec->num_mixers++] = spec->kctl_alloc; 2015f7278fd0SJosepch Chan 2016f7278fd0SJosepch Chan spec->input_mux = &spec->private_imux; 2017f7278fd0SJosepch Chan 2018f7278fd0SJosepch Chan return 1; 2019f7278fd0SJosepch Chan } 2020f7278fd0SJosepch Chan 2021f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 2022f7278fd0SJosepch Chan static struct hda_amp_list vt1708B_loopbacks[] = { 2023f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 2024f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 2025f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 2026f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 2027f7278fd0SJosepch Chan { } /* end */ 2028f7278fd0SJosepch Chan }; 2029f7278fd0SJosepch Chan #endif 2030f7278fd0SJosepch Chan 2031f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 2032f7278fd0SJosepch Chan { 2033f7278fd0SJosepch Chan struct via_spec *spec; 2034f7278fd0SJosepch Chan int err; 2035f7278fd0SJosepch Chan 2036f7278fd0SJosepch Chan /* create a codec specific record */ 2037eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 2038f7278fd0SJosepch Chan if (spec == NULL) 2039f7278fd0SJosepch Chan return -ENOMEM; 2040f7278fd0SJosepch Chan 2041f7278fd0SJosepch Chan codec->spec = spec; 2042f7278fd0SJosepch Chan 2043f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 2044f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 2045f7278fd0SJosepch Chan if (err < 0) { 2046f7278fd0SJosepch Chan via_free(codec); 2047f7278fd0SJosepch Chan return err; 2048f7278fd0SJosepch Chan } else if (!err) { 2049f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2050f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 2051f7278fd0SJosepch Chan } 2052f7278fd0SJosepch Chan 2053*69e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; 2054*69e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 2055f7278fd0SJosepch Chan 2056f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 2057f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; 2058f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 2059f7278fd0SJosepch Chan 2060f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 2061f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 2062f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 2063f7278fd0SJosepch Chan 2064f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 2065f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 2066f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 2067f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 2068f7278fd0SJosepch Chan spec->num_mixers++; 2069f7278fd0SJosepch Chan } 2070f7278fd0SJosepch Chan 2071f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2072f7278fd0SJosepch Chan 2073f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 2074*69e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2075f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 2076f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 2077f7278fd0SJosepch Chan #endif 2078f7278fd0SJosepch Chan 2079f7278fd0SJosepch Chan return 0; 2080f7278fd0SJosepch Chan } 2081f7278fd0SJosepch Chan 2082f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 2083f7278fd0SJosepch Chan { 2084f7278fd0SJosepch Chan struct via_spec *spec; 2085f7278fd0SJosepch Chan int err; 2086f7278fd0SJosepch Chan 2087f7278fd0SJosepch Chan /* create a codec specific record */ 2088eb14a46cSHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 2089f7278fd0SJosepch Chan if (spec == NULL) 2090f7278fd0SJosepch Chan return -ENOMEM; 2091f7278fd0SJosepch Chan 2092f7278fd0SJosepch Chan codec->spec = spec; 2093f7278fd0SJosepch Chan 2094f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 2095f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 2096f7278fd0SJosepch Chan if (err < 0) { 2097f7278fd0SJosepch Chan via_free(codec); 2098f7278fd0SJosepch Chan return err; 2099f7278fd0SJosepch Chan } else if (!err) { 2100f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2101f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 2102f7278fd0SJosepch Chan } 2103f7278fd0SJosepch Chan 2104*69e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; 2105*69e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 2106f7278fd0SJosepch Chan 2107f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 2108f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; 2109f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 2110f7278fd0SJosepch Chan 2111f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 2112f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 2113f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 2114f7278fd0SJosepch Chan 2115f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 2116f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 2117f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 2118f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 2119f7278fd0SJosepch Chan spec->num_mixers++; 2120f7278fd0SJosepch Chan } 2121f7278fd0SJosepch Chan 2122f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2123f7278fd0SJosepch Chan 2124f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 2125*69e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2126f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 2127f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 2128f7278fd0SJosepch Chan #endif 2129c577b8a1SJoseph Chan 2130c577b8a1SJoseph Chan return 0; 2131c577b8a1SJoseph Chan } 2132c577b8a1SJoseph Chan 2133d949cac1SHarald Welte /* Patch for VT1708S */ 2134d949cac1SHarald Welte 2135d949cac1SHarald Welte /* capture mixer elements */ 2136d949cac1SHarald Welte static struct snd_kcontrol_new vt1708S_capture_mixer[] = { 2137d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 2138d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 2139d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 2140d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 2141d949cac1SHarald Welte HDA_CODEC_VOLUME("Mic Boost", 0x1A, 0x0, HDA_INPUT), 2142d949cac1SHarald Welte HDA_CODEC_VOLUME("Front Mic Boost", 0x1E, 0x0, HDA_INPUT), 2143d949cac1SHarald Welte { 2144d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2145d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 2146d949cac1SHarald Welte * So call somewhat different.. 2147d949cac1SHarald Welte */ 2148d949cac1SHarald Welte /* .name = "Capture Source", */ 2149d949cac1SHarald Welte .name = "Input Source", 2150d949cac1SHarald Welte .count = 1, 2151d949cac1SHarald Welte .info = via_mux_enum_info, 2152d949cac1SHarald Welte .get = via_mux_enum_get, 2153d949cac1SHarald Welte .put = via_mux_enum_put, 2154d949cac1SHarald Welte }, 2155d949cac1SHarald Welte { } /* end */ 2156d949cac1SHarald Welte }; 2157d949cac1SHarald Welte 2158d949cac1SHarald Welte static struct hda_verb vt1708S_volume_init_verbs[] = { 2159d949cac1SHarald Welte /* Unmute ADC0-1 and set the default input to mic-in */ 2160d949cac1SHarald Welte {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2161d949cac1SHarald Welte {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2162d949cac1SHarald Welte 2163d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the 2164d949cac1SHarald Welte * analog-loopback mixer widget */ 2165d949cac1SHarald Welte /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2166d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2167d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2168d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2169d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2170d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2171d949cac1SHarald Welte 2172d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 2173d949cac1SHarald Welte {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 2174d949cac1SHarald Welte /* PW9 Output enable */ 2175d949cac1SHarald Welte {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2176d949cac1SHarald Welte { } 2177d949cac1SHarald Welte }; 2178d949cac1SHarald Welte 2179*69e52a80SHarald Welte static struct hda_verb vt1708S_uniwill_init_verbs[] = { 2180*69e52a80SHarald Welte {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, 2181*69e52a80SHarald Welte { } 2182*69e52a80SHarald Welte }; 2183*69e52a80SHarald Welte 2184d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_playback = { 2185d949cac1SHarald Welte .substreams = 2, 2186d949cac1SHarald Welte .channels_min = 2, 2187d949cac1SHarald Welte .channels_max = 8, 2188d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 2189d949cac1SHarald Welte .ops = { 2190d949cac1SHarald Welte .open = via_playback_pcm_open, 2191d949cac1SHarald Welte .prepare = via_playback_pcm_prepare, 2192d949cac1SHarald Welte .cleanup = via_playback_pcm_cleanup 2193d949cac1SHarald Welte }, 2194d949cac1SHarald Welte }; 2195d949cac1SHarald Welte 2196d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_capture = { 2197d949cac1SHarald Welte .substreams = 2, 2198d949cac1SHarald Welte .channels_min = 2, 2199d949cac1SHarald Welte .channels_max = 2, 2200d949cac1SHarald Welte .nid = 0x13, /* NID to query formats and rates */ 2201d949cac1SHarald Welte .ops = { 2202d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 2203d949cac1SHarald Welte .cleanup = via_capture_pcm_cleanup 2204d949cac1SHarald Welte }, 2205d949cac1SHarald Welte }; 2206d949cac1SHarald Welte 2207d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_digital_playback = { 2208d949cac1SHarald Welte .substreams = 1, 2209d949cac1SHarald Welte .channels_min = 2, 2210d949cac1SHarald Welte .channels_max = 2, 2211d949cac1SHarald Welte /* NID is set in via_build_pcms */ 2212d949cac1SHarald Welte .ops = { 2213d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 2214d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 2215d949cac1SHarald Welte .prepare = via_dig_playback_pcm_prepare 2216d949cac1SHarald Welte }, 2217d949cac1SHarald Welte }; 2218d949cac1SHarald Welte 2219d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 2220d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec, 2221d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 2222d949cac1SHarald Welte { 2223d949cac1SHarald Welte int i; 2224d949cac1SHarald Welte hda_nid_t nid; 2225d949cac1SHarald Welte 2226d949cac1SHarald Welte spec->multiout.num_dacs = cfg->line_outs; 2227d949cac1SHarald Welte 2228d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 2229d949cac1SHarald Welte 2230d949cac1SHarald Welte for (i = 0; i < 4; i++) { 2231d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 2232d949cac1SHarald Welte if (nid) { 2233d949cac1SHarald Welte /* config dac list */ 2234d949cac1SHarald Welte switch (i) { 2235d949cac1SHarald Welte case AUTO_SEQ_FRONT: 2236d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x10; 2237d949cac1SHarald Welte break; 2238d949cac1SHarald Welte case AUTO_SEQ_CENLFE: 2239d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x24; 2240d949cac1SHarald Welte break; 2241d949cac1SHarald Welte case AUTO_SEQ_SURROUND: 2242d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x11; 2243d949cac1SHarald Welte break; 2244d949cac1SHarald Welte case AUTO_SEQ_SIDE: 2245d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x25; 2246d949cac1SHarald Welte break; 2247d949cac1SHarald Welte } 2248d949cac1SHarald Welte } 2249d949cac1SHarald Welte } 2250d949cac1SHarald Welte 2251d949cac1SHarald Welte return 0; 2252d949cac1SHarald Welte } 2253d949cac1SHarald Welte 2254d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 2255d949cac1SHarald Welte static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec, 2256d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 2257d949cac1SHarald Welte { 2258d949cac1SHarald Welte char name[32]; 2259d949cac1SHarald Welte static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 2260d949cac1SHarald Welte hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25}; 2261d949cac1SHarald Welte hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27}; 2262d949cac1SHarald Welte hda_nid_t nid, nid_vol, nid_mute; 2263d949cac1SHarald Welte int i, err; 2264d949cac1SHarald Welte 2265d949cac1SHarald Welte for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 2266d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 2267d949cac1SHarald Welte 2268d949cac1SHarald Welte if (!nid) 2269d949cac1SHarald Welte continue; 2270d949cac1SHarald Welte 2271d949cac1SHarald Welte nid_vol = nid_vols[i]; 2272d949cac1SHarald Welte nid_mute = nid_mutes[i]; 2273d949cac1SHarald Welte 2274d949cac1SHarald Welte if (i == AUTO_SEQ_CENLFE) { 2275d949cac1SHarald Welte /* Center/LFE */ 2276d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2277d949cac1SHarald Welte "Center Playback Volume", 2278d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2279d949cac1SHarald Welte HDA_OUTPUT)); 2280d949cac1SHarald Welte if (err < 0) 2281d949cac1SHarald Welte return err; 2282d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2283d949cac1SHarald Welte "LFE Playback Volume", 2284d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2285d949cac1SHarald Welte HDA_OUTPUT)); 2286d949cac1SHarald Welte if (err < 0) 2287d949cac1SHarald Welte return err; 2288d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2289d949cac1SHarald Welte "Center Playback Switch", 2290d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 2291d949cac1SHarald Welte 1, 0, 2292d949cac1SHarald Welte HDA_OUTPUT)); 2293d949cac1SHarald Welte if (err < 0) 2294d949cac1SHarald Welte return err; 2295d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2296d949cac1SHarald Welte "LFE Playback Switch", 2297d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 2298d949cac1SHarald Welte 2, 0, 2299d949cac1SHarald Welte HDA_OUTPUT)); 2300d949cac1SHarald Welte if (err < 0) 2301d949cac1SHarald Welte return err; 2302d949cac1SHarald Welte } else if (i == AUTO_SEQ_FRONT) { 2303d949cac1SHarald Welte /* add control to mixer index 0 */ 2304d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2305d949cac1SHarald Welte "Master Front Playback Volume", 2306d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 2307d949cac1SHarald Welte HDA_INPUT)); 2308d949cac1SHarald Welte if (err < 0) 2309d949cac1SHarald Welte return err; 2310d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2311d949cac1SHarald Welte "Master Front Playback Switch", 2312d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 2313d949cac1SHarald Welte HDA_INPUT)); 2314d949cac1SHarald Welte if (err < 0) 2315d949cac1SHarald Welte return err; 2316d949cac1SHarald Welte 2317d949cac1SHarald Welte /* Front */ 2318d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 2319d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2320d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2321d949cac1SHarald Welte HDA_OUTPUT)); 2322d949cac1SHarald Welte if (err < 0) 2323d949cac1SHarald Welte return err; 2324d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 2325d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2326d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 2327d949cac1SHarald Welte 3, 0, 2328d949cac1SHarald Welte HDA_OUTPUT)); 2329d949cac1SHarald Welte if (err < 0) 2330d949cac1SHarald Welte return err; 2331d949cac1SHarald Welte } else { 2332d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 2333d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2334d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2335d949cac1SHarald Welte HDA_OUTPUT)); 2336d949cac1SHarald Welte if (err < 0) 2337d949cac1SHarald Welte return err; 2338d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 2339d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2340d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 2341d949cac1SHarald Welte 3, 0, 2342d949cac1SHarald Welte HDA_OUTPUT)); 2343d949cac1SHarald Welte if (err < 0) 2344d949cac1SHarald Welte return err; 2345d949cac1SHarald Welte } 2346d949cac1SHarald Welte } 2347d949cac1SHarald Welte 2348d949cac1SHarald Welte return 0; 2349d949cac1SHarald Welte } 2350d949cac1SHarald Welte 2351d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 2352d949cac1SHarald Welte { 2353d949cac1SHarald Welte int err; 2354d949cac1SHarald Welte 2355d949cac1SHarald Welte if (!pin) 2356d949cac1SHarald Welte return 0; 2357d949cac1SHarald Welte 2358d949cac1SHarald Welte spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ 2359d949cac1SHarald Welte 2360d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2361d949cac1SHarald Welte "Headphone Playback Volume", 2362d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 2363d949cac1SHarald Welte if (err < 0) 2364d949cac1SHarald Welte return err; 2365d949cac1SHarald Welte 2366d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2367d949cac1SHarald Welte "Headphone Playback Switch", 2368d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2369d949cac1SHarald Welte if (err < 0) 2370d949cac1SHarald Welte return err; 2371d949cac1SHarald Welte 2372d949cac1SHarald Welte return 0; 2373d949cac1SHarald Welte } 2374d949cac1SHarald Welte 2375d949cac1SHarald Welte /* create playback/capture controls for input pins */ 2376d949cac1SHarald Welte static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec, 2377d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 2378d949cac1SHarald Welte { 2379d949cac1SHarald Welte static char *labels[] = { 2380d949cac1SHarald Welte "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 2381d949cac1SHarald Welte }; 2382d949cac1SHarald Welte struct hda_input_mux *imux = &spec->private_imux; 2383d949cac1SHarald Welte int i, err, idx = 0; 2384d949cac1SHarald Welte 2385d949cac1SHarald Welte /* for internal loopback recording select */ 2386d949cac1SHarald Welte imux->items[imux->num_items].label = "Stereo Mixer"; 2387d949cac1SHarald Welte imux->items[imux->num_items].index = 5; 2388d949cac1SHarald Welte imux->num_items++; 2389d949cac1SHarald Welte 2390d949cac1SHarald Welte for (i = 0; i < AUTO_PIN_LAST; i++) { 2391d949cac1SHarald Welte if (!cfg->input_pins[i]) 2392d949cac1SHarald Welte continue; 2393d949cac1SHarald Welte 2394d949cac1SHarald Welte switch (cfg->input_pins[i]) { 2395d949cac1SHarald Welte case 0x1a: /* Mic */ 2396d949cac1SHarald Welte idx = 2; 2397d949cac1SHarald Welte break; 2398d949cac1SHarald Welte 2399d949cac1SHarald Welte case 0x1b: /* Line In */ 2400d949cac1SHarald Welte idx = 3; 2401d949cac1SHarald Welte break; 2402d949cac1SHarald Welte 2403d949cac1SHarald Welte case 0x1e: /* Front Mic */ 2404d949cac1SHarald Welte idx = 4; 2405d949cac1SHarald Welte break; 2406d949cac1SHarald Welte 2407d949cac1SHarald Welte case 0x1f: /* CD */ 2408d949cac1SHarald Welte idx = 1; 2409d949cac1SHarald Welte break; 2410d949cac1SHarald Welte } 2411d949cac1SHarald Welte err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], 2412d949cac1SHarald Welte idx, 0x16); 2413d949cac1SHarald Welte if (err < 0) 2414d949cac1SHarald Welte return err; 2415d949cac1SHarald Welte imux->items[imux->num_items].label = labels[i]; 2416d949cac1SHarald Welte imux->items[imux->num_items].index = idx-1; 2417d949cac1SHarald Welte imux->num_items++; 2418d949cac1SHarald Welte } 2419d949cac1SHarald Welte return 0; 2420d949cac1SHarald Welte } 2421d949cac1SHarald Welte 2422d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec) 2423d949cac1SHarald Welte { 2424d949cac1SHarald Welte struct via_spec *spec = codec->spec; 2425d949cac1SHarald Welte int err; 2426d949cac1SHarald Welte static hda_nid_t vt1708s_ignore[] = {0x21, 0}; 2427d949cac1SHarald Welte 2428d949cac1SHarald Welte err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, 2429d949cac1SHarald Welte vt1708s_ignore); 2430d949cac1SHarald Welte if (err < 0) 2431d949cac1SHarald Welte return err; 2432d949cac1SHarald Welte err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); 2433d949cac1SHarald Welte if (err < 0) 2434d949cac1SHarald Welte return err; 2435d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2436d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 2437d949cac1SHarald Welte 2438d949cac1SHarald Welte err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg); 2439d949cac1SHarald Welte if (err < 0) 2440d949cac1SHarald Welte return err; 2441d949cac1SHarald Welte err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2442d949cac1SHarald Welte if (err < 0) 2443d949cac1SHarald Welte return err; 2444d949cac1SHarald Welte err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg); 2445d949cac1SHarald Welte if (err < 0) 2446d949cac1SHarald Welte return err; 2447d949cac1SHarald Welte 2448d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2449d949cac1SHarald Welte 2450d949cac1SHarald Welte if (spec->autocfg.dig_out_pin) 2451d949cac1SHarald Welte spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID; 2452d949cac1SHarald Welte 2453d949cac1SHarald Welte if (spec->kctl_alloc) 2454d949cac1SHarald Welte spec->mixers[spec->num_mixers++] = spec->kctl_alloc; 2455d949cac1SHarald Welte 2456d949cac1SHarald Welte spec->input_mux = &spec->private_imux; 2457d949cac1SHarald Welte 2458d949cac1SHarald Welte return 1; 2459d949cac1SHarald Welte } 2460d949cac1SHarald Welte 2461d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 2462d949cac1SHarald Welte static struct hda_amp_list vt1708S_loopbacks[] = { 2463d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 2464d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 2465d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 2466d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 2467d949cac1SHarald Welte { } /* end */ 2468d949cac1SHarald Welte }; 2469d949cac1SHarald Welte #endif 2470d949cac1SHarald Welte 2471d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 2472d949cac1SHarald Welte { 2473d949cac1SHarald Welte struct via_spec *spec; 2474d949cac1SHarald Welte int err; 2475d949cac1SHarald Welte 2476d949cac1SHarald Welte /* create a codec specific record */ 2477d949cac1SHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 2478d949cac1SHarald Welte if (spec == NULL) 2479d949cac1SHarald Welte return -ENOMEM; 2480d949cac1SHarald Welte 2481d949cac1SHarald Welte codec->spec = spec; 2482d949cac1SHarald Welte 2483d949cac1SHarald Welte /* automatic parse from the BIOS config */ 2484d949cac1SHarald Welte err = vt1708S_parse_auto_config(codec); 2485d949cac1SHarald Welte if (err < 0) { 2486d949cac1SHarald Welte via_free(codec); 2487d949cac1SHarald Welte return err; 2488d949cac1SHarald Welte } else if (!err) { 2489d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 2490d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 2491d949cac1SHarald Welte } 2492d949cac1SHarald Welte 2493*69e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; 2494*69e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs; 2495d949cac1SHarald Welte 2496d949cac1SHarald Welte spec->stream_name_analog = "VT1708S Analog"; 2497d949cac1SHarald Welte spec->stream_analog_playback = &vt1708S_pcm_analog_playback; 2498d949cac1SHarald Welte spec->stream_analog_capture = &vt1708S_pcm_analog_capture; 2499d949cac1SHarald Welte 2500d949cac1SHarald Welte spec->stream_name_digital = "VT1708S Digital"; 2501d949cac1SHarald Welte spec->stream_digital_playback = &vt1708S_pcm_digital_playback; 2502d949cac1SHarald Welte 2503d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 2504d949cac1SHarald Welte spec->adc_nids = vt1708S_adc_nids; 2505d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); 2506d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; 2507d949cac1SHarald Welte spec->num_mixers++; 2508d949cac1SHarald Welte } 2509d949cac1SHarald Welte 2510d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 2511d949cac1SHarald Welte 2512d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 2513*69e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2514d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 2515d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 2516d949cac1SHarald Welte #endif 2517d949cac1SHarald Welte 2518d949cac1SHarald Welte return 0; 2519d949cac1SHarald Welte } 2520d949cac1SHarald Welte 2521d949cac1SHarald Welte /* Patch for VT1702 */ 2522d949cac1SHarald Welte 2523d949cac1SHarald Welte /* capture mixer elements */ 2524d949cac1SHarald Welte static struct snd_kcontrol_new vt1702_capture_mixer[] = { 2525d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), 2526d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), 2527d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), 2528d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), 2529d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), 2530d949cac1SHarald Welte HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), 2531d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, 2532d949cac1SHarald Welte HDA_INPUT), 2533d949cac1SHarald Welte { 2534d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2535d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 2536d949cac1SHarald Welte * So call somewhat different.. 2537d949cac1SHarald Welte */ 2538d949cac1SHarald Welte /* .name = "Capture Source", */ 2539d949cac1SHarald Welte .name = "Input Source", 2540d949cac1SHarald Welte .count = 1, 2541d949cac1SHarald Welte .info = via_mux_enum_info, 2542d949cac1SHarald Welte .get = via_mux_enum_get, 2543d949cac1SHarald Welte .put = via_mux_enum_put, 2544d949cac1SHarald Welte }, 2545d949cac1SHarald Welte { } /* end */ 2546d949cac1SHarald Welte }; 2547d949cac1SHarald Welte 2548d949cac1SHarald Welte static struct hda_verb vt1702_volume_init_verbs[] = { 2549d949cac1SHarald Welte /* 2550d949cac1SHarald Welte * Unmute ADC0-1 and set the default input to mic-in 2551d949cac1SHarald Welte */ 2552d949cac1SHarald Welte {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2553d949cac1SHarald Welte {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2554d949cac1SHarald Welte {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2555d949cac1SHarald Welte 2556d949cac1SHarald Welte 2557d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2558d949cac1SHarald Welte * mixer widget 2559d949cac1SHarald Welte */ 2560d949cac1SHarald Welte /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ 2561d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2562d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2563d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2564d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2565d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 2566d949cac1SHarald Welte 2567d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 2568d949cac1SHarald Welte {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, 2569d949cac1SHarald Welte /* PW6 PW7 Output enable */ 2570d949cac1SHarald Welte {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2571d949cac1SHarald Welte {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2572d949cac1SHarald Welte { } 2573d949cac1SHarald Welte }; 2574d949cac1SHarald Welte 2575*69e52a80SHarald Welte static struct hda_verb vt1702_uniwill_init_verbs[] = { 2576*69e52a80SHarald Welte {0x01, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_GPIO_EVENT}, 2577*69e52a80SHarald Welte {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, 2578*69e52a80SHarald Welte { } 2579*69e52a80SHarald Welte }; 2580*69e52a80SHarald Welte 2581d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_playback = { 2582d949cac1SHarald Welte .substreams = 1, 2583d949cac1SHarald Welte .channels_min = 2, 2584d949cac1SHarald Welte .channels_max = 2, 2585d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 2586d949cac1SHarald Welte .ops = { 2587d949cac1SHarald Welte .open = via_playback_pcm_open, 2588d949cac1SHarald Welte .prepare = via_playback_pcm_prepare, 2589d949cac1SHarald Welte .cleanup = via_playback_pcm_cleanup 2590d949cac1SHarald Welte }, 2591d949cac1SHarald Welte }; 2592d949cac1SHarald Welte 2593d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_capture = { 2594d949cac1SHarald Welte .substreams = 3, 2595d949cac1SHarald Welte .channels_min = 2, 2596d949cac1SHarald Welte .channels_max = 2, 2597d949cac1SHarald Welte .nid = 0x12, /* NID to query formats and rates */ 2598d949cac1SHarald Welte .ops = { 2599d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 2600d949cac1SHarald Welte .cleanup = via_capture_pcm_cleanup 2601d949cac1SHarald Welte }, 2602d949cac1SHarald Welte }; 2603d949cac1SHarald Welte 2604d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_digital_playback = { 2605d949cac1SHarald Welte .substreams = 1, 2606d949cac1SHarald Welte .channels_min = 2, 2607d949cac1SHarald Welte .channels_max = 2, 2608d949cac1SHarald Welte /* NID is set in via_build_pcms */ 2609d949cac1SHarald Welte .ops = { 2610d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 2611d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 2612d949cac1SHarald Welte .prepare = via_dig_playback_pcm_prepare 2613d949cac1SHarald Welte }, 2614d949cac1SHarald Welte }; 2615d949cac1SHarald Welte 2616d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 2617d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec, 2618d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 2619d949cac1SHarald Welte { 2620d949cac1SHarald Welte spec->multiout.num_dacs = 1; 2621d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 2622d949cac1SHarald Welte 2623d949cac1SHarald Welte if (cfg->line_out_pins[0]) { 2624d949cac1SHarald Welte /* config dac list */ 2625d949cac1SHarald Welte spec->multiout.dac_nids[0] = 0x10; 2626d949cac1SHarald Welte } 2627d949cac1SHarald Welte 2628d949cac1SHarald Welte return 0; 2629d949cac1SHarald Welte } 2630d949cac1SHarald Welte 2631d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 2632d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, 2633d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 2634d949cac1SHarald Welte { 2635d949cac1SHarald Welte int err; 2636d949cac1SHarald Welte 2637d949cac1SHarald Welte if (!cfg->line_out_pins[0]) 2638d949cac1SHarald Welte return -1; 2639d949cac1SHarald Welte 2640d949cac1SHarald Welte /* add control to mixer index 0 */ 2641d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2642d949cac1SHarald Welte "Master Front Playback Volume", 2643d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 2644d949cac1SHarald Welte if (err < 0) 2645d949cac1SHarald Welte return err; 2646d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2647d949cac1SHarald Welte "Master Front Playback Switch", 2648d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 2649d949cac1SHarald Welte if (err < 0) 2650d949cac1SHarald Welte return err; 2651d949cac1SHarald Welte 2652d949cac1SHarald Welte /* Front */ 2653d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2654d949cac1SHarald Welte "Front Playback Volume", 2655d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT)); 2656d949cac1SHarald Welte if (err < 0) 2657d949cac1SHarald Welte return err; 2658d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2659d949cac1SHarald Welte "Front Playback Switch", 2660d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT)); 2661d949cac1SHarald Welte if (err < 0) 2662d949cac1SHarald Welte return err; 2663d949cac1SHarald Welte 2664d949cac1SHarald Welte return 0; 2665d949cac1SHarald Welte } 2666d949cac1SHarald Welte 2667d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 2668d949cac1SHarald Welte { 2669d949cac1SHarald Welte int err; 2670d949cac1SHarald Welte 2671d949cac1SHarald Welte if (!pin) 2672d949cac1SHarald Welte return 0; 2673d949cac1SHarald Welte 2674d949cac1SHarald Welte spec->multiout.hp_nid = 0x1D; 2675d949cac1SHarald Welte 2676d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2677d949cac1SHarald Welte "Headphone Playback Volume", 2678d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT)); 2679d949cac1SHarald Welte if (err < 0) 2680d949cac1SHarald Welte return err; 2681d949cac1SHarald Welte 2682d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2683d949cac1SHarald Welte "Headphone Playback Switch", 2684d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2685d949cac1SHarald Welte if (err < 0) 2686d949cac1SHarald Welte return err; 2687d949cac1SHarald Welte 2688d949cac1SHarald Welte return 0; 2689d949cac1SHarald Welte } 2690d949cac1SHarald Welte 2691d949cac1SHarald Welte /* create playback/capture controls for input pins */ 2692d949cac1SHarald Welte static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec, 2693d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 2694d949cac1SHarald Welte { 2695d949cac1SHarald Welte static char *labels[] = { 2696d949cac1SHarald Welte "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 2697d949cac1SHarald Welte }; 2698d949cac1SHarald Welte struct hda_input_mux *imux = &spec->private_imux; 2699d949cac1SHarald Welte int i, err, idx = 0; 2700d949cac1SHarald Welte 2701d949cac1SHarald Welte /* for internal loopback recording select */ 2702d949cac1SHarald Welte imux->items[imux->num_items].label = "Stereo Mixer"; 2703d949cac1SHarald Welte imux->items[imux->num_items].index = 3; 2704d949cac1SHarald Welte imux->num_items++; 2705d949cac1SHarald Welte 2706d949cac1SHarald Welte for (i = 0; i < AUTO_PIN_LAST; i++) { 2707d949cac1SHarald Welte if (!cfg->input_pins[i]) 2708d949cac1SHarald Welte continue; 2709d949cac1SHarald Welte 2710d949cac1SHarald Welte switch (cfg->input_pins[i]) { 2711d949cac1SHarald Welte case 0x14: /* Mic */ 2712d949cac1SHarald Welte idx = 1; 2713d949cac1SHarald Welte break; 2714d949cac1SHarald Welte 2715d949cac1SHarald Welte case 0x15: /* Line In */ 2716d949cac1SHarald Welte idx = 2; 2717d949cac1SHarald Welte break; 2718d949cac1SHarald Welte 2719d949cac1SHarald Welte case 0x18: /* Front Mic */ 2720d949cac1SHarald Welte idx = 3; 2721d949cac1SHarald Welte break; 2722d949cac1SHarald Welte } 2723d949cac1SHarald Welte err = via_new_analog_input(spec, cfg->input_pins[i], 2724d949cac1SHarald Welte labels[i], idx, 0x1A); 2725d949cac1SHarald Welte if (err < 0) 2726d949cac1SHarald Welte return err; 2727d949cac1SHarald Welte imux->items[imux->num_items].label = labels[i]; 2728d949cac1SHarald Welte imux->items[imux->num_items].index = idx-1; 2729d949cac1SHarald Welte imux->num_items++; 2730d949cac1SHarald Welte } 2731d949cac1SHarald Welte return 0; 2732d949cac1SHarald Welte } 2733d949cac1SHarald Welte 2734d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec) 2735d949cac1SHarald Welte { 2736d949cac1SHarald Welte struct via_spec *spec = codec->spec; 2737d949cac1SHarald Welte int err; 2738d949cac1SHarald Welte static hda_nid_t vt1702_ignore[] = {0x1C, 0}; 2739d949cac1SHarald Welte 2740d949cac1SHarald Welte err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, 2741d949cac1SHarald Welte vt1702_ignore); 2742d949cac1SHarald Welte if (err < 0) 2743d949cac1SHarald Welte return err; 2744d949cac1SHarald Welte err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); 2745d949cac1SHarald Welte if (err < 0) 2746d949cac1SHarald Welte return err; 2747d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2748d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 2749d949cac1SHarald Welte 2750d949cac1SHarald Welte err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg); 2751d949cac1SHarald Welte if (err < 0) 2752d949cac1SHarald Welte return err; 2753d949cac1SHarald Welte err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2754d949cac1SHarald Welte if (err < 0) 2755d949cac1SHarald Welte return err; 2756d949cac1SHarald Welte err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg); 2757d949cac1SHarald Welte if (err < 0) 2758d949cac1SHarald Welte return err; 2759d949cac1SHarald Welte 2760d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2761d949cac1SHarald Welte 2762d949cac1SHarald Welte if (spec->autocfg.dig_out_pin) 2763d949cac1SHarald Welte spec->multiout.dig_out_nid = VT1702_DIGOUT_NID; 2764d949cac1SHarald Welte 2765d949cac1SHarald Welte if (spec->kctl_alloc) 2766d949cac1SHarald Welte spec->mixers[spec->num_mixers++] = spec->kctl_alloc; 2767d949cac1SHarald Welte 2768d949cac1SHarald Welte spec->input_mux = &spec->private_imux; 2769d949cac1SHarald Welte 2770d949cac1SHarald Welte return 1; 2771d949cac1SHarald Welte } 2772d949cac1SHarald Welte 2773d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 2774d949cac1SHarald Welte static struct hda_amp_list vt1702_loopbacks[] = { 2775d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 2776d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 2777d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 2778d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 2779d949cac1SHarald Welte { } /* end */ 2780d949cac1SHarald Welte }; 2781d949cac1SHarald Welte #endif 2782d949cac1SHarald Welte 2783d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 2784d949cac1SHarald Welte { 2785d949cac1SHarald Welte struct via_spec *spec; 2786d949cac1SHarald Welte int err; 2787d949cac1SHarald Welte unsigned int response; 2788d949cac1SHarald Welte unsigned char control; 2789d949cac1SHarald Welte 2790d949cac1SHarald Welte /* create a codec specific record */ 2791d949cac1SHarald Welte spec = kzalloc(sizeof(*spec), GFP_KERNEL); 2792d949cac1SHarald Welte if (spec == NULL) 2793d949cac1SHarald Welte return -ENOMEM; 2794d949cac1SHarald Welte 2795d949cac1SHarald Welte codec->spec = spec; 2796d949cac1SHarald Welte 2797d949cac1SHarald Welte /* automatic parse from the BIOS config */ 2798d949cac1SHarald Welte err = vt1702_parse_auto_config(codec); 2799d949cac1SHarald Welte if (err < 0) { 2800d949cac1SHarald Welte via_free(codec); 2801d949cac1SHarald Welte return err; 2802d949cac1SHarald Welte } else if (!err) { 2803d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 2804d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 2805d949cac1SHarald Welte } 2806d949cac1SHarald Welte 2807*69e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; 2808*69e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; 2809d949cac1SHarald Welte 2810d949cac1SHarald Welte spec->stream_name_analog = "VT1702 Analog"; 2811d949cac1SHarald Welte spec->stream_analog_playback = &vt1702_pcm_analog_playback; 2812d949cac1SHarald Welte spec->stream_analog_capture = &vt1702_pcm_analog_capture; 2813d949cac1SHarald Welte 2814d949cac1SHarald Welte spec->stream_name_digital = "VT1702 Digital"; 2815d949cac1SHarald Welte spec->stream_digital_playback = &vt1702_pcm_digital_playback; 2816d949cac1SHarald Welte 2817d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 2818d949cac1SHarald Welte spec->adc_nids = vt1702_adc_nids; 2819d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids); 2820d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1702_capture_mixer; 2821d949cac1SHarald Welte spec->num_mixers++; 2822d949cac1SHarald Welte } 2823d949cac1SHarald Welte 2824d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 2825d949cac1SHarald Welte 2826d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 2827*69e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2828d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 2829d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 2830d949cac1SHarald Welte #endif 2831d949cac1SHarald Welte 2832d949cac1SHarald Welte /* Open backdoor */ 2833d949cac1SHarald Welte response = snd_hda_codec_read(codec, codec->afg, 0, 0xF8C, 0); 2834d949cac1SHarald Welte control = (unsigned char)(response & 0xff); 2835d949cac1SHarald Welte control |= 0x3; 2836d949cac1SHarald Welte snd_hda_codec_write(codec, codec->afg, 0, 0xF88, control); 2837d949cac1SHarald Welte 2838d949cac1SHarald Welte /* Enable GPIO 0&1 for volume&mute control */ 2839d949cac1SHarald Welte /* Enable GPIO 2 for DMIC-DATA */ 2840d949cac1SHarald Welte response = snd_hda_codec_read(codec, codec->afg, 0, 0xF84, 0); 2841d949cac1SHarald Welte control = (unsigned char)((response >> 16) & 0x3f); 2842d949cac1SHarald Welte snd_hda_codec_write(codec, codec->afg, 0, 0xF82, control); 2843d949cac1SHarald Welte 2844d949cac1SHarald Welte return 0; 2845d949cac1SHarald Welte } 2846d949cac1SHarald Welte 2847c577b8a1SJoseph Chan /* 2848c577b8a1SJoseph Chan * patch entries 2849c577b8a1SJoseph Chan */ 2850c577b8a1SJoseph Chan struct hda_codec_preset snd_hda_preset_via[] = { 2851c577b8a1SJoseph Chan { .id = 0x11061708, .name = "VIA VT1708", .patch = patch_vt1708}, 2852c577b8a1SJoseph Chan { .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708}, 2853c577b8a1SJoseph Chan { .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708}, 2854c577b8a1SJoseph Chan { .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708}, 2855f7278fd0SJosepch Chan { .id = 0x1106E710, .name = "VIA VT1709 10-Ch", 2856f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 2857f7278fd0SJosepch Chan { .id = 0x1106E711, .name = "VIA VT1709 10-Ch", 2858f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 2859f7278fd0SJosepch Chan { .id = 0x1106E712, .name = "VIA VT1709 10-Ch", 2860f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 2861f7278fd0SJosepch Chan { .id = 0x1106E713, .name = "VIA VT1709 10-Ch", 2862f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 2863f7278fd0SJosepch Chan { .id = 0x1106E714, .name = "VIA VT1709 6-Ch", 2864f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 2865f7278fd0SJosepch Chan { .id = 0x1106E715, .name = "VIA VT1709 6-Ch", 2866f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 2867f7278fd0SJosepch Chan { .id = 0x1106E716, .name = "VIA VT1709 6-Ch", 2868f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 2869f7278fd0SJosepch Chan { .id = 0x1106E717, .name = "VIA VT1709 6-Ch", 2870f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 2871f7278fd0SJosepch Chan { .id = 0x1106E720, .name = "VIA VT1708B 8-Ch", 2872f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 2873f7278fd0SJosepch Chan { .id = 0x1106E721, .name = "VIA VT1708B 8-Ch", 2874f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 2875f7278fd0SJosepch Chan { .id = 0x1106E722, .name = "VIA VT1708B 8-Ch", 2876f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 2877f7278fd0SJosepch Chan { .id = 0x1106E723, .name = "VIA VT1708B 8-Ch", 2878f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 2879f7278fd0SJosepch Chan { .id = 0x1106E724, .name = "VIA VT1708B 4-Ch", 2880f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 2881f7278fd0SJosepch Chan { .id = 0x1106E725, .name = "VIA VT1708B 4-Ch", 2882f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 2883f7278fd0SJosepch Chan { .id = 0x1106E726, .name = "VIA VT1708B 4-Ch", 2884f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 2885f7278fd0SJosepch Chan { .id = 0x1106E727, .name = "VIA VT1708B 4-Ch", 2886f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 2887d949cac1SHarald Welte { .id = 0x11060397, .name = "VIA VT1708S", 2888d949cac1SHarald Welte .patch = patch_vt1708S}, 2889d949cac1SHarald Welte { .id = 0x11061397, .name = "VIA VT1708S", 2890d949cac1SHarald Welte .patch = patch_vt1708S}, 2891d949cac1SHarald Welte { .id = 0x11062397, .name = "VIA VT1708S", 2892d949cac1SHarald Welte .patch = patch_vt1708S}, 2893d949cac1SHarald Welte { .id = 0x11063397, .name = "VIA VT1708S", 2894d949cac1SHarald Welte .patch = patch_vt1708S}, 2895d949cac1SHarald Welte { .id = 0x11064397, .name = "VIA VT1708S", 2896d949cac1SHarald Welte .patch = patch_vt1708S}, 2897d949cac1SHarald Welte { .id = 0x11065397, .name = "VIA VT1708S", 2898d949cac1SHarald Welte .patch = patch_vt1708S}, 2899d949cac1SHarald Welte { .id = 0x11066397, .name = "VIA VT1708S", 2900d949cac1SHarald Welte .patch = patch_vt1708S}, 2901d949cac1SHarald Welte { .id = 0x11067397, .name = "VIA VT1708S", 2902d949cac1SHarald Welte .patch = patch_vt1708S}, 2903d949cac1SHarald Welte { .id = 0x11060398, .name = "VIA VT1702", 2904d949cac1SHarald Welte .patch = patch_vt1702}, 2905d949cac1SHarald Welte { .id = 0x11061398, .name = "VIA VT1702", 2906d949cac1SHarald Welte .patch = patch_vt1702}, 2907d949cac1SHarald Welte { .id = 0x11062398, .name = "VIA VT1702", 2908d949cac1SHarald Welte .patch = patch_vt1702}, 2909d949cac1SHarald Welte { .id = 0x11063398, .name = "VIA VT1702", 2910d949cac1SHarald Welte .patch = patch_vt1702}, 2911d949cac1SHarald Welte { .id = 0x11064398, .name = "VIA VT1702", 2912d949cac1SHarald Welte .patch = patch_vt1702}, 2913d949cac1SHarald Welte { .id = 0x11065398, .name = "VIA VT1702", 2914d949cac1SHarald Welte .patch = patch_vt1702}, 2915d949cac1SHarald Welte { .id = 0x11066398, .name = "VIA VT1702", 2916d949cac1SHarald Welte .patch = patch_vt1702}, 2917d949cac1SHarald Welte { .id = 0x11067398, .name = "VIA VT1702", 2918d949cac1SHarald Welte .patch = patch_vt1702}, 2919c577b8a1SJoseph Chan {} /* terminator */ 2920c577b8a1SJoseph Chan }; 2921