Commit c577b8a16fd19a33a8865ca6451287d284a0faf6

Authored by Joseph Chan
Committed by Jaroslav Kysela
1 parent 6c5cfd9d9d

[ALSA] hda-codec - Add support for VIA VT1708(A) HD audio codec

This patch is VIA first release for HD audio codec, VT1708(A) and
it provides geneneral HD audio driver features.

Signed-off-by: Joseph Chan <josephchan@via.com.tw>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>

Showing 4 changed files with 1402 additions and 1 deletions Side-by-side Diff

sound/pci/hda/Makefile
... ... @@ -7,7 +7,8 @@
7 7 patch_sigmatel.o \
8 8 patch_si3054.o \
9 9 patch_atihdmi.o \
10   - patch_conexant.o
  10 + patch_conexant.o \
  11 + patch_via.o
11 12 ifdef CONFIG_PROC_FS
12 13 snd-hda-codec-objs += hda_proc.o
13 14 endif
sound/pci/hda/hda_codec.c
... ... @@ -52,6 +52,7 @@
52 52 static struct hda_vendor_id hda_vendor_ids[] = {
53 53 { 0x10ec, "Realtek" },
54 54 { 0x1057, "Motorola" },
  55 + { 0x1106, "VIA" },
55 56 { 0x11d4, "Analog Devices" },
56 57 { 0x13f6, "C-Media" },
57 58 { 0x14f1, "Conexant" },
sound/pci/hda/hda_patch.h
... ... @@ -16,6 +16,8 @@
16 16 extern struct hda_codec_preset snd_hda_preset_atihdmi[];
17 17 /* Conexant audio codec */
18 18 extern struct hda_codec_preset snd_hda_preset_conexant[];
  19 +/* VIA codecs */
  20 +extern struct hda_codec_preset snd_hda_preset_via[];
19 21  
20 22 static const struct hda_codec_preset *hda_preset_tables[] = {
21 23 snd_hda_preset_realtek,
... ... @@ -25,6 +27,7 @@
25 27 snd_hda_preset_si3054,
26 28 snd_hda_preset_atihdmi,
27 29 snd_hda_preset_conexant,
  30 + snd_hda_preset_via,
28 31 NULL
29 32 };
sound/pci/hda/patch_via.c
Changes suppressed. Click to show
  1 +/*
  2 + * Universal Interface for Intel High Definition Audio Codec
  3 + *
  4 + * HD audio interface patch for VIA VT1708 codec
  5 + *
  6 + * Copyright (c) 2006 Lydia Wang <lydiawang@viatech.com>
  7 + * Takashi Iwai <tiwai@suse.de>
  8 + *
  9 + * This driver is free software; you can redistribute it and/or modify
  10 + * it under the terms of the GNU General Public License as published by
  11 + * the Free Software Foundation; either version 2 of the License, or
  12 + * (at your option) any later version.
  13 + *
  14 + * This driver is distributed in the hope that it will be useful,
  15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17 + * GNU General Public License for more details.
  18 + *
  19 + * You should have received a copy of the GNU General Public License
  20 + * along with this program; if not, write to the Free Software
  21 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  22 + */
  23 +
  24 +/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
  25 +/* */
  26 +/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
  27 +/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
  28 +/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
  29 +/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
  30 +/* */
  31 +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  32 +
  33 +
  34 +#include <sound/driver.h>
  35 +#include <linux/init.h>
  36 +#include <linux/delay.h>
  37 +#include <linux/slab.h>
  38 +#include <linux/pci.h>
  39 +#include <sound/core.h>
  40 +#include "hda_codec.h"
  41 +#include "hda_local.h"
  42 +
  43 +
  44 +/* amp values */
  45 +#define AMP_VAL_IDX_SHIFT 19
  46 +#define AMP_VAL_IDX_MASK (0x0f<<19)
  47 +
  48 +#define NUM_CONTROL_ALLOC 32
  49 +#define NUM_VERB_ALLOC 32
  50 +
  51 +/* Pin Widget NID */
  52 +#define VT1708_HP_NID 0x13
  53 +#define VT1708_DIGOUT_NID 0x14
  54 +#define VT1708_DIGIN_NID 0x16
  55 +
  56 +#define VT1709_HP_DAC_NID 0x28
  57 +#define VT1709_DIGOUT_NID 0x13
  58 +#define VT1709_DIGIN_NID 0x17
  59 +
  60 +#define IS_VT1708_VENDORID(x) ((x) >= 0x11061708 && (x) <= 0x1106170b)
  61 +#define IS_VT1709_10CH_VENDORID(x) ((x) >= 0x1106e710 && (x) <= 0x1106e713)
  62 +#define IS_VT1709_6CH_VENDORID(x) ((x) >= 0x1106e714 && (x) <= 0x1106e717)
  63 +
  64 +
  65 +enum {
  66 + VIA_CTL_WIDGET_VOL,
  67 + VIA_CTL_WIDGET_MUTE,
  68 +};
  69 +
  70 +enum {
  71 + AUTO_SEQ_FRONT,
  72 + AUTO_SEQ_SURROUND,
  73 + AUTO_SEQ_CENLFE,
  74 + AUTO_SEQ_SIDE
  75 +};
  76 +
  77 +static struct snd_kcontrol_new vt1708_control_templates[] = {
  78 + HDA_CODEC_VOLUME(NULL, 0, 0, 0),
  79 + HDA_CODEC_MUTE(NULL, 0, 0, 0),
  80 +};
  81 +
  82 +
  83 +struct via_spec {
  84 + /* codec parameterization */
  85 + struct snd_kcontrol_new *mixers[3];
  86 + unsigned int num_mixers;
  87 +
  88 + struct hda_verb *init_verbs;
  89 +
  90 + char *stream_name_analog;
  91 + struct hda_pcm_stream *stream_analog_playback;
  92 + struct hda_pcm_stream *stream_analog_capture;
  93 +
  94 + char *stream_name_digital;
  95 + struct hda_pcm_stream *stream_digital_playback;
  96 + struct hda_pcm_stream *stream_digital_capture;
  97 +
  98 + /* playback */
  99 + struct hda_multi_out multiout;
  100 +
  101 + /* capture */
  102 + unsigned int num_adc_nids;
  103 + hda_nid_t *adc_nids;
  104 + hda_nid_t dig_in_nid;
  105 +
  106 + /* capture source */
  107 + const struct hda_input_mux *input_mux;
  108 + unsigned int cur_mux[3];
  109 +
  110 + /* PCM information */
  111 + struct hda_pcm pcm_rec[2];
  112 +
  113 + /* dynamic controls, init_verbs and input_mux */
  114 + struct auto_pin_cfg autocfg;
  115 + unsigned int num_kctl_alloc, num_kctl_used;
  116 + struct snd_kcontrol_new *kctl_alloc;
  117 + struct hda_input_mux private_imux;
  118 + hda_nid_t private_dac_nids[4];
  119 +};
  120 +
  121 +static hda_nid_t vt1708_adc_nids[2] = {
  122 + /* ADC1-2 */
  123 + 0x15, 0x27
  124 +};
  125 +
  126 +static hda_nid_t vt1709_adc_nids[3] = {
  127 + /* ADC1-2 */
  128 + 0x14, 0x15, 0x16
  129 +};
  130 +
  131 +/* add dynamic controls */
  132 +static int via_add_control(struct via_spec *spec, int type, const char *name,
  133 + unsigned long val)
  134 +{
  135 + struct snd_kcontrol_new *knew;
  136 +
  137 + if (spec->num_kctl_used >= spec->num_kctl_alloc) {
  138 + int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
  139 +
  140 + /* array + terminator */
  141 + knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL);
  142 + if (!knew)
  143 + return -ENOMEM;
  144 + if (spec->kctl_alloc) {
  145 + memcpy(knew, spec->kctl_alloc,
  146 + sizeof(*knew) * spec->num_kctl_alloc);
  147 + kfree(spec->kctl_alloc);
  148 + }
  149 + spec->kctl_alloc = knew;
  150 + spec->num_kctl_alloc = num;
  151 + }
  152 +
  153 + knew = &spec->kctl_alloc[spec->num_kctl_used];
  154 + *knew = vt1708_control_templates[type];
  155 + knew->name = kstrdup(name, GFP_KERNEL);
  156 +
  157 + if (!knew->name)
  158 + return -ENOMEM;
  159 + knew->private_value = val;
  160 + spec->num_kctl_used++;
  161 + return 0;
  162 +}
  163 +
  164 +/* create input playback/capture controls for the given pin */
  165 +static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin,
  166 + const char *ctlname, int idx, int mix_nid)
  167 +{
  168 + char name[32];
  169 + int err;
  170 +
  171 + sprintf(name, "%s Playback Volume", ctlname);
  172 + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
  173 + HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
  174 + if (err < 0)
  175 + return err;
  176 + sprintf(name, "%s Playback Switch", ctlname);
  177 + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
  178 + HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
  179 + if (err < 0)
  180 + return err;
  181 + return 0;
  182 +}
  183 +
  184 +static void via_auto_set_output_and_unmute(struct hda_codec *codec,
  185 + hda_nid_t nid, int pin_type,
  186 + int dac_idx)
  187 +{
  188 + /* set as output */
  189 + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
  190 + pin_type);
  191 + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
  192 + AMP_OUT_UNMUTE);
  193 +}
  194 +
  195 +
  196 +static void via_auto_init_multi_out(struct hda_codec *codec)
  197 +{
  198 + struct via_spec *spec = codec->spec;
  199 + int i;
  200 +
  201 + for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
  202 + hda_nid_t nid = spec->autocfg.line_out_pins[i];
  203 + if (nid)
  204 + via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
  205 + }
  206 +}
  207 +
  208 +static void via_auto_init_hp_out(struct hda_codec *codec)
  209 +{
  210 + struct via_spec *spec = codec->spec;
  211 + hda_nid_t pin;
  212 +
  213 + pin = spec->autocfg.hp_pins[0];
  214 + if (pin) /* connect to front */
  215 + via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
  216 +}
  217 +
  218 +static void via_auto_init_analog_input(struct hda_codec *codec)
  219 +{
  220 + struct via_spec *spec = codec->spec;
  221 + int i;
  222 +
  223 + for (i = 0; i < AUTO_PIN_LAST; i++) {
  224 + hda_nid_t nid = spec->autocfg.input_pins[i];
  225 +
  226 + snd_hda_codec_write(codec, nid, 0,
  227 + AC_VERB_SET_PIN_WIDGET_CONTROL,
  228 + (i <= AUTO_PIN_FRONT_MIC ?
  229 + PIN_VREF50 : PIN_IN));
  230 +
  231 + }
  232 +}
  233 +/*
  234 + * input MUX handling
  235 + */
  236 +static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
  237 + struct snd_ctl_elem_info *uinfo)
  238 +{
  239 + struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
  240 + struct via_spec *spec = codec->spec;
  241 + return snd_hda_input_mux_info(spec->input_mux, uinfo);
  242 +}
  243 +
  244 +static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
  245 + struct snd_ctl_elem_value *ucontrol)
  246 +{
  247 + struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
  248 + struct via_spec *spec = codec->spec;
  249 + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
  250 +
  251 + ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
  252 + return 0;
  253 +}
  254 +
  255 +static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
  256 + struct snd_ctl_elem_value *ucontrol)
  257 +{
  258 + struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
  259 + struct via_spec *spec = codec->spec;
  260 + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
  261 + unsigned int vendor_id = codec->vendor_id;
  262 +
  263 + /* AIW0 lydia 060801 add for correct sw0 input select */
  264 + if (IS_VT1708_VENDORID(vendor_id) && (adc_idx == 0))
  265 + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
  266 + 0x18, &spec->cur_mux[adc_idx]);
  267 + else if ((IS_VT1709_10CH_VENDORID(vendor_id) ||
  268 + IS_VT1709_6CH_VENDORID(vendor_id)) && (adc_idx == 0) )
  269 + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
  270 + 0x19, &spec->cur_mux[adc_idx]);
  271 + else
  272 + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
  273 + spec->adc_nids[adc_idx],
  274 + &spec->cur_mux[adc_idx]);
  275 +}
  276 +
  277 +/* capture mixer elements */
  278 +static struct snd_kcontrol_new vt1708_capture_mixer[] = {
  279 + HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
  280 + HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
  281 + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
  282 + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
  283 + {
  284 + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  285 + /* The multiple "Capture Source" controls confuse alsamixer
  286 + * So call somewhat different..
  287 + * FIXME: the controls appear in the "playback" view!
  288 + */
  289 + /* .name = "Capture Source", */
  290 + .name = "Input Source",
  291 + .count = 1,
  292 + .info = via_mux_enum_info,
  293 + .get = via_mux_enum_get,
  294 + .put = via_mux_enum_put,
  295 + },
  296 + { } /* end */
  297 +};
  298 +/*
  299 + * generic initialization of ADC, input mixers and output mixers
  300 + */
  301 +static struct hda_verb vt1708_volume_init_verbs[] = {
  302 + /*
  303 + * Unmute ADC0-1 and set the default input to mic-in
  304 + */
  305 + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
  306 + {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
  307 +
  308 +
  309 + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
  310 + * mixer widget
  311 + */
  312 + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
  313 + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
  314 + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
  315 + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
  316 + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
  317 + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
  318 +
  319 + /*
  320 + * Set up output mixers (0x19 - 0x1b)
  321 + */
  322 + /* set vol=0 to output mixers */
  323 + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
  324 + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
  325 + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
  326 +
  327 + /* Setup default input to PW4 */
  328 + {0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
  329 + /* Set mic as default input of sw0 */
  330 + {0x18, AC_VERB_SET_CONNECT_SEL, 0x2},
  331 + /* PW9 Output enable */
  332 + {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
  333 +};
  334 +
  335 +static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
  336 + struct hda_codec *codec,
  337 + struct snd_pcm_substream *substream)
  338 +{
  339 + struct via_spec *spec = codec->spec;
  340 + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
  341 +}
  342 +
  343 +static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
  344 + struct hda_codec *codec,
  345 + unsigned int stream_tag,
  346 + unsigned int format,
  347 + struct snd_pcm_substream *substream)
  348 +{
  349 + struct via_spec *spec = codec->spec;
  350 + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
  351 + stream_tag, format, substream);
  352 +}
  353 +
  354 +static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
  355 + struct hda_codec *codec,
  356 + struct snd_pcm_substream *substream)
  357 +{
  358 + struct via_spec *spec = codec->spec;
  359 + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
  360 +}
  361 +
  362 +/*
  363 + * Digital out
  364 + */
  365 +static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
  366 + struct hda_codec *codec,
  367 + struct snd_pcm_substream *substream)
  368 +{
  369 + struct via_spec *spec = codec->spec;
  370 + return snd_hda_multi_out_dig_open(codec, &spec->multiout);
  371 +}
  372 +
  373 +static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
  374 + struct hda_codec *codec,
  375 + struct snd_pcm_substream *substream)
  376 +{
  377 + struct via_spec *spec = codec->spec;
  378 + return snd_hda_multi_out_dig_close(codec, &spec->multiout);
  379 +}
  380 +
  381 +/*
  382 + * Analog capture
  383 + */
  384 +static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
  385 + struct hda_codec *codec,
  386 + unsigned int stream_tag,
  387 + unsigned int format,
  388 + struct snd_pcm_substream *substream)
  389 +{
  390 + struct via_spec *spec = codec->spec;
  391 +
  392 + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
  393 + stream_tag, 0, format);
  394 + return 0;
  395 +}
  396 +
  397 +static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
  398 + struct hda_codec *codec,
  399 + struct snd_pcm_substream *substream)
  400 +{
  401 + struct via_spec *spec = codec->spec;
  402 + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
  403 + 0, 0, 0);
  404 + return 0;
  405 +}
  406 +
  407 +static struct hda_pcm_stream vt1708_pcm_analog_playback = {
  408 + .substreams = 1,
  409 + .channels_min = 2,
  410 + .channels_max = 8,
  411 + .nid = 0x10, /* NID to query formats and rates */
  412 + .ops = {
  413 + .open = via_playback_pcm_open,
  414 + .prepare = via_playback_pcm_prepare,
  415 + .cleanup = via_playback_pcm_cleanup
  416 + },
  417 +};
  418 +
  419 +static struct hda_pcm_stream vt1708_pcm_analog_capture = {
  420 + .substreams = 2,
  421 + .channels_min = 2,
  422 + .channels_max = 2,
  423 + .nid = 0x15, /* NID to query formats and rates */
  424 + .ops = {
  425 + .prepare = via_capture_pcm_prepare,
  426 + .cleanup = via_capture_pcm_cleanup
  427 + },
  428 +};
  429 +
  430 +static struct hda_pcm_stream vt1708_pcm_digital_playback = {
  431 + .substreams = 1,
  432 + .channels_min = 2,
  433 + .channels_max = 2,
  434 + /* NID is set in via_build_pcms */
  435 + .ops = {
  436 + .open = via_dig_playback_pcm_open,
  437 + .close = via_dig_playback_pcm_close
  438 + },
  439 +};
  440 +
  441 +static struct hda_pcm_stream vt1708_pcm_digital_capture = {
  442 + .substreams = 1,
  443 + .channels_min = 2,
  444 + .channels_max = 2,
  445 +};
  446 +
  447 +static int via_build_controls(struct hda_codec *codec)
  448 +{
  449 + struct via_spec *spec = codec->spec;
  450 + int err;
  451 + int i;
  452 +
  453 + for (i = 0; i < spec->num_mixers; i++) {
  454 + err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
  455 + if (err < 0)
  456 + return err;
  457 + }
  458 +
  459 + if (spec->multiout.dig_out_nid) {
  460 + err = snd_hda_create_spdif_out_ctls(codec,
  461 + spec->multiout.dig_out_nid);
  462 + if (err < 0)
  463 + return err;
  464 + }
  465 + if (spec->dig_in_nid) {
  466 + err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
  467 + if (err < 0)
  468 + return err;
  469 + }
  470 + return 0;
  471 +}
  472 +
  473 +static int via_build_pcms(struct hda_codec *codec)
  474 +{
  475 + struct via_spec *spec = codec->spec;
  476 + struct hda_pcm *info = spec->pcm_rec;
  477 +
  478 + codec->num_pcms = 1;
  479 + codec->pcm_info = info;
  480 +
  481 + info->name = spec->stream_name_analog;
  482 + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
  483 + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
  484 + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
  485 + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
  486 +
  487 + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
  488 + spec->multiout.max_channels;
  489 +
  490 + if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
  491 + codec->num_pcms++;
  492 + info++;
  493 + info->name = spec->stream_name_digital;
  494 + if (spec->multiout.dig_out_nid) {
  495 + info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
  496 + *(spec->stream_digital_playback);
  497 + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
  498 + spec->multiout.dig_out_nid;
  499 + }
  500 + if (spec->dig_in_nid) {
  501 + info->stream[SNDRV_PCM_STREAM_CAPTURE] =
  502 + *(spec->stream_digital_capture);
  503 + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
  504 + spec->dig_in_nid;
  505 + }
  506 + }
  507 +
  508 + return 0;
  509 +}
  510 +
  511 +static void via_free(struct hda_codec *codec)
  512 +{
  513 + struct via_spec *spec = codec->spec;
  514 + unsigned int i;
  515 +
  516 + if (!spec)
  517 + return;
  518 +
  519 + if (spec->kctl_alloc) {
  520 + for (i = 0; i < spec->num_kctl_used; i++)
  521 + kfree(spec->kctl_alloc[i].name);
  522 + kfree(spec->kctl_alloc);
  523 + }
  524 +
  525 + kfree(codec->spec);
  526 +}
  527 +
  528 +static int via_init(struct hda_codec *codec)
  529 +{
  530 + struct via_spec *spec = codec->spec;
  531 + snd_hda_sequence_write(codec, spec->init_verbs);
  532 + return 0;
  533 +}
  534 +
  535 +#ifdef CONFIG_PM
  536 +/*
  537 + * resume
  538 + */
  539 +static int via_resume(struct hda_codec *codec)
  540 +{
  541 + struct via_spec *spec = codec->spec;
  542 + int i;
  543 +
  544 + via_init(codec);
  545 + for (i = 0; i < spec->num_mixers; i++)
  546 + snd_hda_resume_ctls(codec, spec->mixers[i]);
  547 + if (spec->multiout.dig_out_nid)
  548 + snd_hda_resume_spdif_out(codec);
  549 + if (spec->dig_in_nid)
  550 + snd_hda_resume_spdif_in(codec);
  551 +
  552 + return 0;
  553 +}
  554 +#endif
  555 +
  556 +/*
  557 + */
  558 +static struct hda_codec_ops via_patch_ops = {
  559 + .build_controls = via_build_controls,
  560 + .build_pcms = via_build_pcms,
  561 + .init = via_init,
  562 + .free = via_free,
  563 +#ifdef CONFIG_PM
  564 + .resume = via_resume,
  565 +#endif
  566 +};
  567 +
  568 +/* fill in the dac_nids table from the parsed pin configuration */
  569 +static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
  570 + const struct auto_pin_cfg *cfg)
  571 +{
  572 + int i;
  573 + hda_nid_t nid;
  574 +
  575 + spec->multiout.num_dacs = cfg->line_outs;
  576 +
  577 + spec->multiout.dac_nids = spec->private_dac_nids;
  578 +
  579 + for(i = 0; i < 4; i++) {
  580 + nid = cfg->line_out_pins[i];
  581 + if (nid) {
  582 + /* config dac list */
  583 + switch (i) {
  584 + case AUTO_SEQ_FRONT:
  585 + spec->multiout.dac_nids[i] = 0x10;
  586 + break;
  587 + case AUTO_SEQ_CENLFE:
  588 + spec->multiout.dac_nids[i] = 0x12;
  589 + break;
  590 + case AUTO_SEQ_SURROUND:
  591 + spec->multiout.dac_nids[i] = 0x13;
  592 + break;
  593 + case AUTO_SEQ_SIDE:
  594 + spec->multiout.dac_nids[i] = 0x11;
  595 + break;
  596 + }
  597 + }
  598 + }
  599 +
  600 + return 0;
  601 +}
  602 +
  603 +/* add playback controls from the parsed DAC table */
  604 +static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
  605 + const struct auto_pin_cfg *cfg)
  606 +{
  607 + char name[32];
  608 + static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
  609 + hda_nid_t nid, nid_vol = 0;
  610 + int i, err;
  611 +
  612 + for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
  613 + nid = cfg->line_out_pins[i];
  614 +
  615 + if (!nid)
  616 + continue;
  617 +
  618 + if (i != AUTO_SEQ_FRONT)
  619 + nid_vol = 0x1b - i + 1;
  620 +
  621 + if (i == AUTO_SEQ_CENLFE) {
  622 + /* Center/LFE */
  623 + err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
  624 + "Center Playback Volume",
  625 + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
  626 + if (err < 0)
  627 + return err;
  628 + err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
  629 + "LFE Playback Volume",
  630 + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
  631 + if (err < 0)
  632 + return err;
  633 + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
  634 + "Center Playback Switch",
  635 + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
  636 + if (err < 0)
  637 + return err;
  638 + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
  639 + "LFE Playback Switch",
  640 + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
  641 + if (err < 0)
  642 + return err;
  643 + } else if (i == AUTO_SEQ_FRONT){
  644 + /* add control to mixer index 0 */
  645 + err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
  646 + "Master Front Playback Volume",
  647 + HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT));
  648 + if (err < 0)
  649 + return err;
  650 + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
  651 + "Master Front Playback Switch",
  652 + HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT));
  653 + if (err < 0)
  654 + return err;
  655 +
  656 + /* add control to PW3 */
  657 + sprintf(name, "%s Playback Volume", chname[i]);
  658 + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
  659 + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
  660 + if (err < 0)
  661 + return err;
  662 + sprintf(name, "%s Playback Switch", chname[i]);
  663 + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
  664 + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
  665 + if (err < 0)
  666 + return err;
  667 + } else {
  668 + sprintf(name, "%s Playback Volume", chname[i]);
  669 + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
  670 + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
  671 + if (err < 0)
  672 + return err;
  673 + sprintf(name, "%s Playback Switch", chname[i]);
  674 + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
  675 + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
  676 + if (err < 0)
  677 + return err;
  678 + }
  679 + }
  680 +
  681 + return 0;
  682 +}
  683 +
  684 +static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
  685 +{
  686 + int err;
  687 +
  688 + if (!pin)
  689 + return 0;
  690 +
  691 + spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
  692 +
  693 + err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
  694 + "Headphone Playback Volume",
  695 + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
  696 + if (err < 0)
  697 + return err;
  698 + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
  699 + "Headphone Playback Switch",
  700 + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
  701 + if (err < 0)
  702 + return err;
  703 +
  704 + return 0;
  705 +}
  706 +
  707 +/* create playback/capture controls for input pins */
  708 +static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
  709 + const struct auto_pin_cfg *cfg)
  710 +{
  711 + static char *labels[] = {
  712 + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
  713 + };
  714 + struct hda_input_mux *imux = &spec->private_imux;
  715 + int i, err, idx = 0;
  716 +
  717 + /* for internal loopback recording select */
  718 + imux->items[imux->num_items].label = "Stereo Mixer";
  719 + imux->items[imux->num_items].index = idx;
  720 + imux->num_items++;
  721 +
  722 + for (i = 0; i < AUTO_PIN_LAST; i++) {
  723 + if (!cfg->input_pins[i])
  724 + continue;
  725 +
  726 + switch (cfg->input_pins[i]) {
  727 + case 0x1d: /* Mic */
  728 + idx = 2;
  729 + break;
  730 +
  731 + case 0x1e: /* Line In */
  732 + idx = 3;
  733 + break;
  734 +
  735 + case 0x21: /* Front Mic */
  736 + idx = 4;
  737 + break;
  738 +
  739 + case 0x24: /* CD */
  740 + idx = 1;
  741 + break;
  742 + }
  743 + err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
  744 + idx, 0x17);
  745 + if (err < 0)
  746 + return err;
  747 + imux->items[imux->num_items].label = labels[i];
  748 + imux->items[imux->num_items].index = idx;
  749 + imux->num_items++;
  750 + }
  751 + return 0;
  752 +}
  753 +
  754 +static int vt1708_parse_auto_config(struct hda_codec *codec)
  755 +{
  756 + struct via_spec *spec = codec->spec;
  757 + int err;
  758 +
  759 + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
  760 + if (err < 0)
  761 + return err;
  762 + err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
  763 + if (err < 0)
  764 + return err;
  765 + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
  766 + return 0; /* can't find valid BIOS pin config */
  767 +
  768 + err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
  769 + if (err < 0)
  770 + return err;
  771 + err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
  772 + if (err < 0)
  773 + return err;
  774 + err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
  775 + if (err < 0)
  776 + return err;
  777 +
  778 + spec->multiout.max_channels = spec->multiout.num_dacs * 2;
  779 +
  780 + if (spec->autocfg.dig_out_pin)
  781 + spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
  782 + if (spec->autocfg.dig_in_pin)
  783 + spec->dig_in_nid = VT1708_DIGIN_NID;
  784 +
  785 + if (spec->kctl_alloc)
  786 + spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
  787 +
  788 + spec->init_verbs = vt1708_volume_init_verbs;
  789 +
  790 + spec->input_mux = &spec->private_imux;
  791 +
  792 + return 1;
  793 +}
  794 +
  795 +/* init callback for auto-configuration model -- overriding the default init */
  796 +static int via_auto_init(struct hda_codec *codec)
  797 +{
  798 + via_init(codec);
  799 + via_auto_init_multi_out(codec);
  800 + via_auto_init_hp_out(codec);
  801 + via_auto_init_analog_input(codec);
  802 + return 0;
  803 +}
  804 +
  805 +static int patch_vt1708(struct hda_codec *codec)
  806 +{
  807 + struct via_spec *spec;
  808 + int err;
  809 +
  810 + /* create a codec specific record */
  811 + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
  812 + if (spec == NULL)
  813 + return -ENOMEM;
  814 +
  815 + codec->spec = spec;
  816 +
  817 + /* automatic parse from the BIOS config */
  818 + err = vt1708_parse_auto_config(codec);
  819 + if (err < 0) {
  820 + via_free(codec);
  821 + return err;
  822 + } else if (!err) {
  823 + printk(KERN_INFO "hda_codec: Cannot set up configuration "
  824 + "from BIOS. Using genenic mode...\n");
  825 + }
  826 +
  827 +
  828 + spec->stream_name_analog = "VT1708 Analog";
  829 + spec->stream_analog_playback = &vt1708_pcm_analog_playback;
  830 + spec->stream_analog_capture = &vt1708_pcm_analog_capture;
  831 +
  832 + spec->stream_name_digital = "VT1708 Digital";
  833 + spec->stream_digital_playback = &vt1708_pcm_digital_playback;
  834 + spec->stream_digital_capture = &vt1708_pcm_digital_capture;
  835 +
  836 +
  837 + if (!spec->adc_nids && spec->input_mux) {
  838 + spec->adc_nids = vt1708_adc_nids;
  839 + spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
  840 + spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
  841 + spec->num_mixers++;
  842 + }
  843 +
  844 + codec->patch_ops = via_patch_ops;
  845 +
  846 + codec->patch_ops.init = via_auto_init;
  847 +
  848 + return 0;
  849 +}
  850 +
  851 +/* capture mixer elements */
  852 +static struct snd_kcontrol_new vt1709_capture_mixer[] = {
  853 + HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
  854 + HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
  855 + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
  856 + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
  857 + HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
  858 + HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
  859 + {
  860 + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  861 + /* The multiple "Capture Source" controls confuse alsamixer
  862 + * So call somewhat different..
  863 + * FIXME: the controls appear in the "playback" view!
  864 + */
  865 + /* .name = "Capture Source", */
  866 + .name = "Input Source",
  867 + .count = 1,
  868 + .info = via_mux_enum_info,
  869 + .get = via_mux_enum_get,
  870 + .put = via_mux_enum_put,
  871 + },
  872 + { } /* end */
  873 +};
  874 +
  875 +/*
  876 + * generic initialization of ADC, input mixers and output mixers
  877 + */
  878 +static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
  879 + /*
  880 + * Unmute ADC0-2 and set the default input to mic-in
  881 + */
  882 + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
  883 + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
  884 + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
  885 +
  886 +
  887 + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
  888 + * mixer widget
  889 + */
  890 + /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
  891 + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
  892 + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
  893 + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
  894 + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
  895 + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
  896 +
  897 + /*
  898 + * Set up output selector (0x1a, 0x1b, 0x29)
  899 + */
  900 + /* set vol=0 to output mixers */
  901 + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
  902 + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
  903 + {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
  904 +
  905 + /*
  906 + * Unmute PW3 and PW4
  907 + */
  908 + {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
  909 + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
  910 +
  911 + /* Set input of PW4 as AOW4 */
  912 + {0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
  913 + /* Set mic as default input of sw0 */
  914 + {0x19, AC_VERB_SET_CONNECT_SEL, 0x2},
  915 + /* PW9 Output enable */
  916 + {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
  917 + { }
  918 +};
  919 +
  920 +static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
  921 + .substreams = 1,
  922 + .channels_min = 2,
  923 + .channels_max = 10,
  924 + .nid = 0x10, /* NID to query formats and rates */
  925 + .ops = {
  926 + .open = via_playback_pcm_open,
  927 + .prepare = via_playback_pcm_prepare,
  928 + .cleanup = via_playback_pcm_cleanup
  929 + },
  930 +};
  931 +
  932 +static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
  933 + .substreams = 1,
  934 + .channels_min = 2,
  935 + .channels_max = 6,
  936 + .nid = 0x10, /* NID to query formats and rates */
  937 + .ops = {
  938 + .open = via_playback_pcm_open,
  939 + .prepare = via_playback_pcm_prepare,
  940 + .cleanup = via_playback_pcm_cleanup
  941 + },
  942 +};
  943 +
  944 +static struct hda_pcm_stream vt1709_pcm_analog_capture = {
  945 + .substreams = 2,
  946 + .channels_min = 2,
  947 + .channels_max = 2,
  948 + .nid = 0x14, /* NID to query formats and rates */
  949 + .ops = {
  950 + .prepare = via_capture_pcm_prepare,
  951 + .cleanup = via_capture_pcm_cleanup
  952 + },
  953 +};
  954 +
  955 +static struct hda_pcm_stream vt1709_pcm_digital_playback = {
  956 + .substreams = 1,
  957 + .channels_min = 2,
  958 + .channels_max = 2,
  959 + /* NID is set in via_build_pcms */
  960 + .ops = {
  961 + .open = via_dig_playback_pcm_open,
  962 + .close = via_dig_playback_pcm_close
  963 + },
  964 +};
  965 +
  966 +static struct hda_pcm_stream vt1709_pcm_digital_capture = {
  967 + .substreams = 1,
  968 + .channels_min = 2,
  969 + .channels_max = 2,
  970 +};
  971 +
  972 +static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
  973 + const struct auto_pin_cfg *cfg)
  974 +{
  975 + int i;
  976 + hda_nid_t nid;
  977 +
  978 + if (cfg->line_outs == 4) /* 10 channels */
  979 + spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
  980 + else if (cfg->line_outs == 3) /* 6 channels */
  981 + spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
  982 +
  983 + spec->multiout.dac_nids = spec->private_dac_nids;
  984 +
  985 + if (cfg->line_outs == 4) { /* 10 channels */
  986 + for (i = 0; i < cfg->line_outs; i++) {
  987 + nid = cfg->line_out_pins[i];
  988 + if (nid) {
  989 + /* config dac list */
  990 + switch (i) {
  991 + case AUTO_SEQ_FRONT:
  992 + /* AOW0 */
  993 + spec->multiout.dac_nids[i] = 0x10;
  994 + break;
  995 + case AUTO_SEQ_CENLFE:
  996 + /* AOW2 */
  997 + spec->multiout.dac_nids[i] = 0x12;
  998 + break;
  999 + case AUTO_SEQ_SURROUND:
  1000 + /* AOW3 */
  1001 + spec->multiout.dac_nids[i] = 0x27;
  1002 + break;
  1003 + case AUTO_SEQ_SIDE:
  1004 + /* AOW1 */
  1005 + spec->multiout.dac_nids[i] = 0x11;
  1006 + break;
  1007 + default:
  1008 + break;
  1009 + }
  1010 + }
  1011 + }
  1012 + spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
  1013 +
  1014 + } else if (cfg->line_outs == 3) { /* 6 channels */
  1015 + for(i = 0; i < cfg->line_outs; i++) {
  1016 + nid = cfg->line_out_pins[i];
  1017 + if (nid) {
  1018 + /* config dac list */
  1019 + switch(i) {
  1020 + case AUTO_SEQ_FRONT:
  1021 + /* AOW0 */
  1022 + spec->multiout.dac_nids[i] = 0x10;
  1023 + break;
  1024 + case AUTO_SEQ_CENLFE:
  1025 + /* AOW2 */
  1026 + spec->multiout.dac_nids[i] = 0x12;
  1027 + break;
  1028 + case AUTO_SEQ_SURROUND:
  1029 + /* AOW1 */
  1030 + spec->multiout.dac_nids[i] = 0x11;
  1031 + break;
  1032 + default:
  1033 + break;
  1034 + }
  1035 + }
  1036 + }
  1037 + }
  1038 +
  1039 + return 0;
  1040 +}
  1041 +
  1042 +/* add playback controls from the parsed DAC table */
  1043 +static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
  1044 + const struct auto_pin_cfg *cfg)
  1045 +{
  1046 + char name[32];
  1047 + static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
  1048 + hda_nid_t nid = 0;
  1049 + int i, err;
  1050 +
  1051 + for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
  1052 + nid = cfg->line_out_pins[i];
  1053 +
  1054 + if (!nid)
  1055 + continue;
  1056 +
  1057 + if (i == AUTO_SEQ_CENLFE) {
  1058 + /* Center/LFE */
  1059 + err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
  1060 + "Center Playback Volume",
  1061 + HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT));
  1062 + if (err < 0)
  1063 + return err;
  1064 + err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
  1065 + "LFE Playback Volume",
  1066 + HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT));
  1067 + if (err < 0)
  1068 + return err;
  1069 + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
  1070 + "Center Playback Switch",
  1071 + HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT));
  1072 + if (err < 0)
  1073 + return err;
  1074 + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
  1075 + "LFE Playback Switch",
  1076 + HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT));
  1077 + if (err < 0)
  1078 + return err;
  1079 + } else if (i == AUTO_SEQ_FRONT){
  1080 + /* add control to mixer index 0 */
  1081 + err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
  1082 + "Master Front Playback Volume",
  1083 + HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT));
  1084 + if (err < 0)
  1085 + return err;
  1086 + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
  1087 + "Master Front Playback Switch",
  1088 + HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT));
  1089 + if (err < 0)
  1090 + return err;
  1091 +
  1092 + /* add control to PW3 */
  1093 + sprintf(name, "%s Playback Volume", chname[i]);
  1094 + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
  1095 + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
  1096 + if (err < 0)
  1097 + return err;
  1098 + sprintf(name, "%s Playback Switch", chname[i]);
  1099 + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
  1100 + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
  1101 + if (err < 0)
  1102 + return err;
  1103 + } else if (i == AUTO_SEQ_SURROUND) {
  1104 + sprintf(name, "%s Playback Volume", chname[i]);
  1105 + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
  1106 + HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT));
  1107 + if (err < 0)
  1108 + return err;
  1109 + sprintf(name, "%s Playback Switch", chname[i]);
  1110 + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
  1111 + HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT));
  1112 + if (err < 0)
  1113 + return err;
  1114 + } else if (i == AUTO_SEQ_SIDE) {
  1115 + sprintf(name, "%s Playback Volume", chname[i]);
  1116 + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
  1117 + HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT));
  1118 + if (err < 0)
  1119 + return err;
  1120 + sprintf(name, "%s Playback Switch", chname[i]);
  1121 + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
  1122 + HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT));
  1123 + if (err < 0)
  1124 + return err;
  1125 + }
  1126 + }
  1127 +
  1128 + return 0;
  1129 +}
  1130 +
  1131 +static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
  1132 +{
  1133 + int err;
  1134 +
  1135 + if (!pin)
  1136 + return 0;
  1137 +
  1138 + if (spec->multiout.num_dacs == 5) /* 10 channels */
  1139 + spec->multiout.hp_nid = VT1709_HP_DAC_NID;
  1140 + else if (spec->multiout.num_dacs == 3) /* 6 channels */
  1141 + spec->multiout.hp_nid = 0;
  1142 +
  1143 + err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
  1144 + "Headphone Playback Volume",
  1145 + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
  1146 + if (err < 0)
  1147 + return err;
  1148 + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
  1149 + "Headphone Playback Switch",
  1150 + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
  1151 + if (err < 0)
  1152 + return err;
  1153 +
  1154 + return 0;
  1155 +}
  1156 +
  1157 +/* create playback/capture controls for input pins */
  1158 +static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
  1159 + const struct auto_pin_cfg *cfg)
  1160 +{
  1161 + static char *labels[] = {
  1162 + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
  1163 + };
  1164 + struct hda_input_mux *imux = &spec->private_imux;
  1165 + int i, err, idx = 0;
  1166 +
  1167 + /* for internal loopback recording select */
  1168 + imux->items[imux->num_items].label = "Stereo Mixer";
  1169 + imux->items[imux->num_items].index = idx;
  1170 + imux->num_items++;
  1171 +
  1172 + for (i = 0; i < AUTO_PIN_LAST; i++) {
  1173 + if (!cfg->input_pins[i])
  1174 + continue;
  1175 +
  1176 + switch (cfg->input_pins[i]) {
  1177 + case 0x1d: /* Mic */
  1178 + idx = 2;
  1179 + break;
  1180 +
  1181 + case 0x1e: /* Line In */
  1182 + idx = 3;
  1183 + break;
  1184 +
  1185 + case 0x21: /* Front Mic */
  1186 + idx = 4;
  1187 + break;
  1188 +
  1189 + case 0x23: /* CD */
  1190 + idx = 1;
  1191 + break;
  1192 + }
  1193 + err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
  1194 + idx, 0x18);
  1195 + if (err < 0)
  1196 + return err;
  1197 + imux->items[imux->num_items].label = labels[i];
  1198 + imux->items[imux->num_items].index = idx;
  1199 + imux->num_items++;
  1200 + }
  1201 + return 0;
  1202 +}
  1203 +
  1204 +static int vt1709_parse_auto_config(struct hda_codec *codec)
  1205 +{
  1206 + struct via_spec *spec = codec->spec;
  1207 + int err;
  1208 +
  1209 + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
  1210 + if (err < 0)
  1211 + return err;
  1212 + err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
  1213 + if (err < 0)
  1214 + return err;
  1215 + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
  1216 + return 0; /* can't find valid BIOS pin config */
  1217 +
  1218 + err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
  1219 + if (err < 0)
  1220 + return err;
  1221 + err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
  1222 + if (err < 0)
  1223 + return err;
  1224 + err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg);
  1225 + if (err < 0)
  1226 + return err;
  1227 +
  1228 + spec->multiout.max_channels = spec->multiout.num_dacs * 2;
  1229 +
  1230 + if (spec->autocfg.dig_out_pin)
  1231 + spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
  1232 + if (spec->autocfg.dig_in_pin)
  1233 + spec->dig_in_nid = VT1709_DIGIN_NID;
  1234 +
  1235 + if (spec->kctl_alloc)
  1236 + spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
  1237 +
  1238 + spec->input_mux = &spec->private_imux;
  1239 +
  1240 + return 1;
  1241 +}
  1242 +
  1243 +static int patch_vt1709_10ch(struct hda_codec *codec)
  1244 +{
  1245 + struct via_spec *spec;
  1246 + int err;
  1247 +
  1248 + /* create a codec specific record */
  1249 + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
  1250 + if (spec == NULL)
  1251 + return -ENOMEM;
  1252 +
  1253 + codec->spec = spec;
  1254 +
  1255 + err = vt1709_parse_auto_config(codec);
  1256 + if (err < 0) {
  1257 + via_free(codec);
  1258 + return err;
  1259 + } else if (!err) {
  1260 + printk(KERN_INFO "hda_codec: Cannot set up configuration. "
  1261 + "Using genenic mode...\n");
  1262 + }
  1263 +
  1264 + spec->init_verbs = vt1709_10ch_volume_init_verbs;
  1265 +
  1266 + spec->stream_name_analog = "VT1709 Analog";
  1267 + spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
  1268 + spec->stream_analog_capture = &vt1709_pcm_analog_capture;
  1269 +
  1270 + spec->stream_name_digital = "VT1709 Digital";
  1271 + spec->stream_digital_playback = &vt1709_pcm_digital_playback;
  1272 + spec->stream_digital_capture = &vt1709_pcm_digital_capture;
  1273 +
  1274 +
  1275 + if (!spec->adc_nids && spec->input_mux) {
  1276 + spec->adc_nids = vt1709_adc_nids;
  1277 + spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
  1278 + spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
  1279 + spec->num_mixers++;
  1280 + }
  1281 +
  1282 + codec->patch_ops = via_patch_ops;
  1283 +
  1284 + codec->patch_ops.init = via_auto_init;
  1285 +
  1286 + return 0;
  1287 +}
  1288 +/*
  1289 + * generic initialization of ADC, input mixers and output mixers
  1290 + */
  1291 +static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
  1292 + /*
  1293 + * Unmute ADC0-2 and set the default input to mic-in
  1294 + */
  1295 + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
  1296 + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
  1297 + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
  1298 +
  1299 +
  1300 + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
  1301 + * mixer widget
  1302 + */
  1303 + /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
  1304 + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
  1305 + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
  1306 + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
  1307 + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
  1308 + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
  1309 +
  1310 + /*
  1311 + * Set up output selector (0x1a, 0x1b, 0x29)
  1312 + */
  1313 + /* set vol=0 to output mixers */
  1314 + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
  1315 + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
  1316 + {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
  1317 +
  1318 + /*
  1319 + * Unmute PW3 and PW4
  1320 + */
  1321 + {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
  1322 + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
  1323 +
  1324 + /* Set input of PW4 as MW0 */
  1325 + {0x20, AC_VERB_SET_CONNECT_SEL, 0},
  1326 + /* Set mic as default input of sw0 */
  1327 + {0x19, AC_VERB_SET_CONNECT_SEL, 0x2},
  1328 + /* PW9 Output enable */
  1329 + {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
  1330 + { }
  1331 +};
  1332 +
  1333 +static int patch_vt1709_6ch(struct hda_codec *codec)
  1334 +{
  1335 + struct via_spec *spec;
  1336 + int err;
  1337 +
  1338 + /* create a codec specific record */
  1339 + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
  1340 + if (spec == NULL)
  1341 + return -ENOMEM;
  1342 +
  1343 + codec->spec = spec;
  1344 +
  1345 + err = vt1709_parse_auto_config(codec);
  1346 + if (err < 0) {
  1347 + via_free(codec);
  1348 + return err;
  1349 + } else if (!err) {
  1350 + printk(KERN_INFO "hda_codec: Cannot set up configuration. "
  1351 + "Using genenic mode...\n");
  1352 + }
  1353 +
  1354 + spec->init_verbs = vt1709_6ch_volume_init_verbs;
  1355 +
  1356 + spec->stream_name_analog = "VT1709 Analog";
  1357 + spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
  1358 + spec->stream_analog_capture = &vt1709_pcm_analog_capture;
  1359 +
  1360 + spec->stream_name_digital = "VT1709 Digital";
  1361 + spec->stream_digital_playback = &vt1709_pcm_digital_playback;
  1362 + spec->stream_digital_capture = &vt1709_pcm_digital_capture;
  1363 +
  1364 +
  1365 + if (!spec->adc_nids && spec->input_mux) {
  1366 + spec->adc_nids = vt1709_adc_nids;
  1367 + spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
  1368 + spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
  1369 + spec->num_mixers++;
  1370 + }
  1371 +
  1372 + codec->patch_ops = via_patch_ops;
  1373 +
  1374 + codec->patch_ops.init = via_auto_init;
  1375 +
  1376 + return 0;
  1377 +}
  1378 +
  1379 +/*
  1380 + * patch entries
  1381 + */
  1382 +struct hda_codec_preset snd_hda_preset_via[] = {
  1383 + { .id = 0x11061708, .name = "VIA VT1708", .patch = patch_vt1708},
  1384 + { .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708},
  1385 + { .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708},
  1386 + { .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708},
  1387 + { .id = 0x1106E710, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
  1388 + { .id = 0x1106E711, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
  1389 + { .id = 0x1106E712, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
  1390 + { .id = 0x1106E713, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
  1391 + { .id = 0x1106E714, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
  1392 + { .id = 0x1106E715, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
  1393 + { .id = 0x1106E716, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
  1394 + { .id = 0x1106E717, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
  1395 + {} /* terminator */
  1396 +};