blob: 9e8dd57e8d5c45cf20423d176c0bd860f99999e9 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Harald Welted949cac2008-09-09 15:56:01 +08004 * HD audio interface patch for VIA VT1702/VT1708/VT1709 codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Harald Welte76d9b0d2008-09-09 15:50:37 +08006 * Copyright (c) 2006-2008 Lydia Wang <lydiawang@viatech.com>
7 * Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
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 */
Josepch Chanf7278fd2007-12-13 16:40:40 +010030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Harald Welted949cac2008-09-09 15:56:01 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
Harald Welte69e52a82008-09-09 15:57:32 +080035/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
Harald Welte0aa62ae2008-09-09 15:58:27 +080036/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Harald Welted7426322008-09-15 22:43:23 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Joseph Chanc577b8a2006-11-29 15:29:40 +010039/* */
40/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
41
42
Joseph Chanc577b8a2006-11-29 15:29:40 +010043#include <linux/init.h>
44#include <linux/delay.h>
45#include <linux/slab.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010046#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080047#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010048#include "hda_codec.h"
49#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010050
51/* amp values */
52#define AMP_VAL_IDX_SHIFT 19
53#define AMP_VAL_IDX_MASK (0x0f<<19)
54
Joseph Chanc577b8a2006-11-29 15:29:40 +010055/* Pin Widget NID */
56#define VT1708_HP_NID 0x13
57#define VT1708_DIGOUT_NID 0x14
58#define VT1708_DIGIN_NID 0x16
Josepch Chanf7278fd2007-12-13 16:40:40 +010059#define VT1708_DIGIN_PIN 0x26
Harald Welted949cac2008-09-09 15:56:01 +080060#define VT1708_HP_PIN_NID 0x20
61#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010062
63#define VT1709_HP_DAC_NID 0x28
64#define VT1709_DIGOUT_NID 0x13
65#define VT1709_DIGIN_NID 0x17
Josepch Chanf7278fd2007-12-13 16:40:40 +010066#define VT1709_DIGIN_PIN 0x25
67
68#define VT1708B_HP_NID 0x25
69#define VT1708B_DIGOUT_NID 0x12
70#define VT1708B_DIGIN_NID 0x15
71#define VT1708B_DIGIN_PIN 0x21
Joseph Chanc577b8a2006-11-29 15:29:40 +010072
Harald Welted949cac2008-09-09 15:56:01 +080073#define VT1708S_HP_NID 0x25
74#define VT1708S_DIGOUT_NID 0x12
75
76#define VT1702_HP_NID 0x17
77#define VT1702_DIGOUT_NID 0x11
78
Harald Welted7426322008-09-15 22:43:23 +080079enum VIA_HDA_CODEC {
80 UNKNOWN = -1,
81 VT1708,
82 VT1709_10CH,
83 VT1709_6CH,
84 VT1708B_8CH,
85 VT1708B_4CH,
86 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080087 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080088 VT1702,
89 CODEC_TYPES,
90};
91
Lydia Wang744ff5f2009-10-10 19:07:26 +080092static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +080093{
Lydia Wang744ff5f2009-10-10 19:07:26 +080094 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +080095 u16 ven_id = vendor_id >> 16;
96 u16 dev_id = vendor_id & 0xffff;
97 enum VIA_HDA_CODEC codec_type;
98
99 /* get codec type */
100 if (ven_id != 0x1106)
101 codec_type = UNKNOWN;
102 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
103 codec_type = VT1708;
104 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
105 codec_type = VT1709_10CH;
106 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
107 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800108 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800109 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800110 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
111 codec_type = VT1708BCE;
112 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800113 codec_type = VT1708B_4CH;
114 else if ((dev_id & 0xfff) == 0x397
115 && (dev_id >> 12) < 8)
116 codec_type = VT1708S;
117 else if ((dev_id & 0xfff) == 0x398
118 && (dev_id >> 12) < 8)
119 codec_type = VT1702;
120 else
121 codec_type = UNKNOWN;
122 return codec_type;
123};
124
Harald Welte69e52a82008-09-09 15:57:32 +0800125#define VIA_HP_EVENT 0x01
126#define VIA_GPIO_EVENT 0x02
127
Joseph Chanc577b8a2006-11-29 15:29:40 +0100128enum {
129 VIA_CTL_WIDGET_VOL,
130 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800131 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100132};
133
134enum {
Harald Welteeb14a462008-09-09 15:40:38 +0800135 AUTO_SEQ_FRONT = 0,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100136 AUTO_SEQ_SURROUND,
137 AUTO_SEQ_CENLFE,
138 AUTO_SEQ_SIDE
139};
140
Harald Welted7426322008-09-15 22:43:23 +0800141/* Some VT1708S based boards gets the micboost setting wrong, so we have
142 * to apply some brute-force and re-write the TLV's by software. */
143static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag,
144 unsigned int size, unsigned int __user *_tlv)
145{
146 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
147 hda_nid_t nid = get_amp_nid(kcontrol);
148
Lydia Wang744ff5f2009-10-10 19:07:26 +0800149 if (get_codec_type(codec) == VT1708S
Harald Welted7426322008-09-15 22:43:23 +0800150 && (nid == 0x1a || nid == 0x1e)) {
151 if (size < 4 * sizeof(unsigned int))
152 return -ENOMEM;
153 if (put_user(1, _tlv)) /* SNDRV_CTL_TLVT_DB_SCALE */
154 return -EFAULT;
155 if (put_user(2 * sizeof(unsigned int), _tlv + 1))
156 return -EFAULT;
157 if (put_user(0, _tlv + 2)) /* offset = 0 */
158 return -EFAULT;
159 if (put_user(1000, _tlv + 3)) /* step size = 10 dB */
160 return -EFAULT;
161 }
162 return 0;
163}
164
165static int mic_boost_volume_info(struct snd_kcontrol *kcontrol,
166 struct snd_ctl_elem_info *uinfo)
167{
168 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
169 hda_nid_t nid = get_amp_nid(kcontrol);
170
Lydia Wang744ff5f2009-10-10 19:07:26 +0800171 if (get_codec_type(codec) == VT1708S
Harald Welted7426322008-09-15 22:43:23 +0800172 && (nid == 0x1a || nid == 0x1e)) {
173 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
174 uinfo->count = 2;
175 uinfo->value.integer.min = 0;
176 uinfo->value.integer.max = 3;
177 }
178 return 0;
179}
180
Lydia Wangf5271102009-10-10 19:07:35 +0800181static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
182static void set_jack_power_state(struct hda_codec *codec);
183
184static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
185 struct snd_ctl_elem_value *ucontrol)
186{
187 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
188 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
189
190 set_jack_power_state(codec);
191 analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
192 return change;
193}
194
195/* modify .put = snd_hda_mixer_amp_switch_put */
196#define ANALOG_INPUT_MUTE \
197 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
198 .name = NULL, \
199 .index = 0, \
200 .info = snd_hda_mixer_amp_switch_info, \
201 .get = snd_hda_mixer_amp_switch_get, \
202 .put = analog_input_switch_put, \
203 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
204
Joseph Chanc577b8a2006-11-29 15:29:40 +0100205static struct snd_kcontrol_new vt1708_control_templates[] = {
206 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
207 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800208 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100209};
210
211
212struct via_spec {
213 /* codec parameterization */
214 struct snd_kcontrol_new *mixers[3];
215 unsigned int num_mixers;
216
Harald Welte69e52a82008-09-09 15:57:32 +0800217 struct hda_verb *init_verbs[5];
218 unsigned int num_iverbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100219
220 char *stream_name_analog;
221 struct hda_pcm_stream *stream_analog_playback;
222 struct hda_pcm_stream *stream_analog_capture;
223
224 char *stream_name_digital;
225 struct hda_pcm_stream *stream_digital_playback;
226 struct hda_pcm_stream *stream_digital_capture;
227
228 /* playback */
229 struct hda_multi_out multiout;
Takashi Iwai9da29272009-05-07 16:31:14 +0200230 hda_nid_t slave_dig_outs[2];
Joseph Chanc577b8a2006-11-29 15:29:40 +0100231
232 /* capture */
233 unsigned int num_adc_nids;
234 hda_nid_t *adc_nids;
Takashi Iwai337b9d02009-07-07 18:18:59 +0200235 hda_nid_t mux_nids[3];
Joseph Chanc577b8a2006-11-29 15:29:40 +0100236 hda_nid_t dig_in_nid;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +0200237 hda_nid_t dig_in_pin;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100238
239 /* capture source */
240 const struct hda_input_mux *input_mux;
241 unsigned int cur_mux[3];
242
243 /* PCM information */
Harald Welte98aa34c2008-09-09 16:02:09 +0800244 struct hda_pcm pcm_rec[3];
Joseph Chanc577b8a2006-11-29 15:29:40 +0100245
246 /* dynamic controls, init_verbs and input_mux */
247 struct auto_pin_cfg autocfg;
Takashi Iwai603c4012008-07-30 15:01:44 +0200248 struct snd_array kctls;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800249 struct hda_input_mux private_imux[2];
Takashi Iwai41923e42007-10-22 17:20:10 +0200250 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
Takashi Iwaicb53c622007-08-10 17:21:45 +0200251
Harald Welte0aa62ae2008-09-09 15:58:27 +0800252 /* HP mode source */
253 const struct hda_input_mux *hp_mux;
254 unsigned int hp_independent_mode;
255
Lydia Wang518bf3b2009-10-10 19:07:29 +0800256 enum VIA_HDA_CODEC codec_type;
257
Takashi Iwaicb53c622007-08-10 17:21:45 +0200258#ifdef CONFIG_SND_HDA_POWER_SAVE
259 struct hda_loopback_check loopback;
260#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +0100261};
262
263static hda_nid_t vt1708_adc_nids[2] = {
264 /* ADC1-2 */
265 0x15, 0x27
266};
267
268static hda_nid_t vt1709_adc_nids[3] = {
269 /* ADC1-2 */
270 0x14, 0x15, 0x16
271};
272
Josepch Chanf7278fd2007-12-13 16:40:40 +0100273static hda_nid_t vt1708B_adc_nids[2] = {
274 /* ADC1-2 */
275 0x13, 0x14
276};
277
Harald Welted949cac2008-09-09 15:56:01 +0800278static hda_nid_t vt1708S_adc_nids[2] = {
279 /* ADC1-2 */
280 0x13, 0x14
281};
282
283static hda_nid_t vt1702_adc_nids[3] = {
284 /* ADC1-2 */
285 0x12, 0x20, 0x1F
286};
287
Joseph Chanc577b8a2006-11-29 15:29:40 +0100288/* add dynamic controls */
289static int via_add_control(struct via_spec *spec, int type, const char *name,
290 unsigned long val)
291{
292 struct snd_kcontrol_new *knew;
293
Takashi Iwai603c4012008-07-30 15:01:44 +0200294 snd_array_init(&spec->kctls, sizeof(*knew), 32);
295 knew = snd_array_new(&spec->kctls);
296 if (!knew)
297 return -ENOMEM;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100298 *knew = vt1708_control_templates[type];
299 knew->name = kstrdup(name, GFP_KERNEL);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100300 if (!knew->name)
301 return -ENOMEM;
302 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100303 return 0;
304}
305
Takashi Iwai603c4012008-07-30 15:01:44 +0200306static void via_free_kctls(struct hda_codec *codec)
307{
308 struct via_spec *spec = codec->spec;
309
310 if (spec->kctls.list) {
311 struct snd_kcontrol_new *kctl = spec->kctls.list;
312 int i;
313 for (i = 0; i < spec->kctls.used; i++)
314 kfree(kctl[i].name);
315 }
316 snd_array_free(&spec->kctls);
317}
318
Joseph Chanc577b8a2006-11-29 15:29:40 +0100319/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800320static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
321 int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100322{
323 char name[32];
324 int err;
325
326 sprintf(name, "%s Playback Volume", ctlname);
327 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
328 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
329 if (err < 0)
330 return err;
331 sprintf(name, "%s Playback Switch", ctlname);
Lydia Wangf5271102009-10-10 19:07:35 +0800332 err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100333 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
334 if (err < 0)
335 return err;
336 return 0;
337}
338
339static void via_auto_set_output_and_unmute(struct hda_codec *codec,
340 hda_nid_t nid, int pin_type,
341 int dac_idx)
342{
343 /* set as output */
344 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
345 pin_type);
346 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
347 AMP_OUT_UNMUTE);
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200348 if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
349 snd_hda_codec_write(codec, nid, 0,
350 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100351}
352
353
354static void via_auto_init_multi_out(struct hda_codec *codec)
355{
356 struct via_spec *spec = codec->spec;
357 int i;
358
359 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
360 hda_nid_t nid = spec->autocfg.line_out_pins[i];
361 if (nid)
362 via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
363 }
364}
365
366static void via_auto_init_hp_out(struct hda_codec *codec)
367{
368 struct via_spec *spec = codec->spec;
369 hda_nid_t pin;
370
371 pin = spec->autocfg.hp_pins[0];
372 if (pin) /* connect to front */
373 via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
374}
375
376static void via_auto_init_analog_input(struct hda_codec *codec)
377{
378 struct via_spec *spec = codec->spec;
379 int i;
380
381 for (i = 0; i < AUTO_PIN_LAST; i++) {
382 hda_nid_t nid = spec->autocfg.input_pins[i];
383
384 snd_hda_codec_write(codec, nid, 0,
385 AC_VERB_SET_PIN_WIDGET_CONTROL,
386 (i <= AUTO_PIN_FRONT_MIC ?
387 PIN_VREF50 : PIN_IN));
388
389 }
390}
Lydia Wangf5271102009-10-10 19:07:35 +0800391
392static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
393 unsigned int *affected_parm)
394{
395 unsigned parm;
396 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
397 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
398 >> AC_DEFCFG_MISC_SHIFT
399 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
400 unsigned present = snd_hda_codec_read(codec, nid, 0,
401 AC_VERB_GET_PIN_SENSE, 0) >> 31;
402
403 if ((no_presence || present) && get_defcfg_connect(def_conf)
404 != AC_JACK_PORT_NONE) {
405 *affected_parm = AC_PWRST_D0; /* if it's connected */
406 parm = AC_PWRST_D0;
407 } else
408 parm = AC_PWRST_D3;
409
410 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
411}
412
413static void set_jack_power_state(struct hda_codec *codec)
414{
415 struct via_spec *spec = codec->spec;
416 int imux_is_smixer;
417 unsigned int parm;
418
419 if (spec->codec_type == VT1702) {
420 imux_is_smixer = snd_hda_codec_read(
421 codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
422 /* inputs */
423 /* PW 1/2/5 (14h/15h/18h) */
424 parm = AC_PWRST_D3;
425 set_pin_power_state(codec, 0x14, &parm);
426 set_pin_power_state(codec, 0x15, &parm);
427 set_pin_power_state(codec, 0x18, &parm);
428 if (imux_is_smixer)
429 parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */
430 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
431 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
432 parm);
433 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE,
434 parm);
435 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
436 parm);
437 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE,
438 parm);
439
440 /* outputs */
441 /* PW 3/4 (16h/17h) */
442 parm = AC_PWRST_D3;
443 set_pin_power_state(codec, 0x16, &parm);
444 set_pin_power_state(codec, 0x17, &parm);
445 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
446 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
447 imux_is_smixer ? AC_PWRST_D0 : parm);
448 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
449 parm);
450 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE,
451 parm);
452 } else if (spec->codec_type == VT1708B_8CH
453 || spec->codec_type == VT1708B_4CH
454 || spec->codec_type == VT1708S) {
455 /* SW0 (17h) = stereo mixer */
456 int is_8ch = spec->codec_type != VT1708B_4CH;
457 imux_is_smixer = snd_hda_codec_read(
458 codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
459 == ((spec->codec_type == VT1708S) ? 5 : 0);
460 /* inputs */
461 /* PW 1/2/5 (1ah/1bh/1eh) */
462 parm = AC_PWRST_D3;
463 set_pin_power_state(codec, 0x1a, &parm);
464 set_pin_power_state(codec, 0x1b, &parm);
465 set_pin_power_state(codec, 0x1e, &parm);
466 if (imux_is_smixer)
467 parm = AC_PWRST_D0;
468 /* SW0 (17h), AIW 0/1 (13h/14h) */
469 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
470 parm);
471 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
472 parm);
473 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
474 parm);
475
476 /* outputs */
477 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
478 parm = AC_PWRST_D3;
479 set_pin_power_state(codec, 0x19, &parm);
480 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
481 parm);
482 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
483 parm);
484
485 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
486 if (is_8ch) {
487 parm = AC_PWRST_D3;
488 set_pin_power_state(codec, 0x22, &parm);
489 snd_hda_codec_write(codec, 0x26, 0,
490 AC_VERB_SET_POWER_STATE, parm);
491 snd_hda_codec_write(codec, 0x24, 0,
492 AC_VERB_SET_POWER_STATE, parm);
493 }
494
495 /* PW 3/4/7 (1ch/1dh/23h) */
496 parm = AC_PWRST_D3;
497 /* force to D0 for internal Speaker */
498 set_pin_power_state(codec, 0x1c, &parm);
499 set_pin_power_state(codec, 0x1d, &parm);
500 if (is_8ch)
501 set_pin_power_state(codec, 0x23, &parm);
502 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
503 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
504 imux_is_smixer ? AC_PWRST_D0 : parm);
505 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
506 parm);
507 if (is_8ch) {
508 snd_hda_codec_write(codec, 0x25, 0,
509 AC_VERB_SET_POWER_STATE, parm);
510 snd_hda_codec_write(codec, 0x27, 0,
511 AC_VERB_SET_POWER_STATE, parm);
512 }
513 }
514}
515
Joseph Chanc577b8a2006-11-29 15:29:40 +0100516/*
517 * input MUX handling
518 */
519static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
520 struct snd_ctl_elem_info *uinfo)
521{
522 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
523 struct via_spec *spec = codec->spec;
524 return snd_hda_input_mux_info(spec->input_mux, uinfo);
525}
526
527static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
528 struct snd_ctl_elem_value *ucontrol)
529{
530 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
531 struct via_spec *spec = codec->spec;
532 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
533
534 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
535 return 0;
536}
537
538static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
539 struct snd_ctl_elem_value *ucontrol)
540{
541 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
542 struct via_spec *spec = codec->spec;
543 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100544
Takashi Iwai337b9d02009-07-07 18:18:59 +0200545 if (!spec->mux_nids[adc_idx])
546 return -EINVAL;
547 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
548 spec->mux_nids[adc_idx],
549 &spec->cur_mux[adc_idx]);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100550}
551
Harald Welte0aa62ae2008-09-09 15:58:27 +0800552static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
553 struct snd_ctl_elem_info *uinfo)
554{
555 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
556 struct via_spec *spec = codec->spec;
557 return snd_hda_input_mux_info(spec->hp_mux, uinfo);
558}
559
560static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
561 struct snd_ctl_elem_value *ucontrol)
562{
563 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
564 struct via_spec *spec = codec->spec;
565 hda_nid_t nid = spec->autocfg.hp_pins[0];
566 unsigned int pinsel = snd_hda_codec_read(codec, nid, 0,
567 AC_VERB_GET_CONNECT_SEL,
568 0x00);
569
570 ucontrol->value.enumerated.item[0] = pinsel;
571
572 return 0;
573}
574
Lydia Wang0713efe2009-10-10 19:07:43 +0800575static void activate_ctl(struct hda_codec *codec, const char *name, int active)
576{
577 struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
578 if (ctl) {
579 ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
580 ctl->vd[0].access |= active
581 ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
582 snd_ctl_notify(codec->bus->card,
583 SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
584 }
585}
586
Harald Welte0aa62ae2008-09-09 15:58:27 +0800587static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
588 struct snd_ctl_elem_value *ucontrol)
589{
590 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
591 struct via_spec *spec = codec->spec;
592 hda_nid_t nid = spec->autocfg.hp_pins[0];
593 unsigned int pinsel = ucontrol->value.enumerated.item[0];
594 unsigned int con_nid = snd_hda_codec_read(codec, nid, 0,
595 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
596
597 if (con_nid == spec->multiout.hp_nid) {
598 if (pinsel == 0) {
599 if (!spec->hp_independent_mode) {
600 if (spec->multiout.num_dacs > 1)
601 spec->multiout.num_dacs -= 1;
602 spec->hp_independent_mode = 1;
603 }
604 } else if (pinsel == 1) {
605 if (spec->hp_independent_mode) {
606 if (spec->multiout.num_dacs > 1)
607 spec->multiout.num_dacs += 1;
608 spec->hp_independent_mode = 0;
609 }
610 }
611 } else {
612 if (pinsel == 0) {
613 if (spec->hp_independent_mode) {
614 if (spec->multiout.num_dacs > 1)
615 spec->multiout.num_dacs += 1;
616 spec->hp_independent_mode = 0;
617 }
618 } else if (pinsel == 1) {
619 if (!spec->hp_independent_mode) {
620 if (spec->multiout.num_dacs > 1)
621 spec->multiout.num_dacs -= 1;
622 spec->hp_independent_mode = 1;
623 }
624 }
625 }
626 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
627 pinsel);
628
629 if (spec->multiout.hp_nid &&
630 spec->multiout.hp_nid != spec->multiout.dac_nids[HDA_FRONT])
631 snd_hda_codec_setup_stream(codec,
632 spec->multiout.hp_nid,
633 0, 0, 0);
634
Lydia Wang0713efe2009-10-10 19:07:43 +0800635 /* update HP volume/swtich active state */
636 if (spec->codec_type == VT1708S
637 || spec->codec_type == VT1702) {
638 activate_ctl(codec, "Headphone Playback Volume",
639 spec->hp_independent_mode);
640 activate_ctl(codec, "Headphone Playback Switch",
641 spec->hp_independent_mode);
642 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800643 return 0;
644}
645
646static struct snd_kcontrol_new via_hp_mixer[] = {
647 {
648 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
649 .name = "Independent HP",
650 .count = 1,
651 .info = via_independent_hp_info,
652 .get = via_independent_hp_get,
653 .put = via_independent_hp_put,
654 },
655 { } /* end */
656};
657
Joseph Chanc577b8a2006-11-29 15:29:40 +0100658/* capture mixer elements */
659static struct snd_kcontrol_new vt1708_capture_mixer[] = {
660 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
661 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
662 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
663 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
664 {
665 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
666 /* The multiple "Capture Source" controls confuse alsamixer
667 * So call somewhat different..
Joseph Chanc577b8a2006-11-29 15:29:40 +0100668 */
669 /* .name = "Capture Source", */
670 .name = "Input Source",
671 .count = 1,
672 .info = via_mux_enum_info,
673 .get = via_mux_enum_get,
674 .put = via_mux_enum_put,
675 },
676 { } /* end */
677};
Lydia Wangf5271102009-10-10 19:07:35 +0800678
679/* check AA path's mute statue */
680static int is_aa_path_mute(struct hda_codec *codec)
681{
682 int mute = 1;
683 hda_nid_t nid_mixer;
684 int start_idx;
685 int end_idx;
686 int i;
687 struct via_spec *spec = codec->spec;
688 /* get nid of MW0 and start & end index */
689 switch (spec->codec_type) {
690 case VT1708B_8CH:
691 case VT1708B_4CH:
692 case VT1708S:
693 nid_mixer = 0x16;
694 start_idx = 2;
695 end_idx = 4;
696 break;
697 case VT1702:
698 nid_mixer = 0x1a;
699 start_idx = 1;
700 end_idx = 3;
701 break;
702 default:
703 return 0;
704 }
705 /* check AA path's mute status */
706 for (i = start_idx; i <= end_idx; i++) {
707 unsigned int con_list = snd_hda_codec_read(
708 codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
709 int shift = 8 * (i % 4);
710 hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
711 unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
712 if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
713 /* check mute status while the pin is connected */
714 int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
715 HDA_INPUT, i) >> 7;
716 int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
717 HDA_INPUT, i) >> 7;
718 if (!mute_l || !mute_r) {
719 mute = 0;
720 break;
721 }
722 }
723 }
724 return mute;
725}
726
727/* enter/exit analog low-current mode */
728static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
729{
730 struct via_spec *spec = codec->spec;
731 static int saved_stream_idle = 1; /* saved stream idle status */
732 int enable = is_aa_path_mute(codec);
733 unsigned int verb = 0;
734 unsigned int parm = 0;
735
736 if (stream_idle == -1) /* stream status did not change */
737 enable = enable && saved_stream_idle;
738 else {
739 enable = enable && stream_idle;
740 saved_stream_idle = stream_idle;
741 }
742
743 /* decide low current mode's verb & parameter */
744 switch (spec->codec_type) {
745 case VT1708B_8CH:
746 case VT1708B_4CH:
747 verb = 0xf70;
748 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
749 break;
750 case VT1708S:
751 verb = 0xf73;
752 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
753 break;
754 case VT1702:
755 verb = 0xf73;
756 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
757 break;
758 default:
759 return; /* other codecs are not supported */
760 }
761 /* send verb */
762 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
763}
764
Joseph Chanc577b8a2006-11-29 15:29:40 +0100765/*
766 * generic initialization of ADC, input mixers and output mixers
767 */
768static struct hda_verb vt1708_volume_init_verbs[] = {
769 /*
770 * Unmute ADC0-1 and set the default input to mic-in
771 */
772 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
773 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
774
775
Josepch Chanf7278fd2007-12-13 16:40:40 +0100776 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
Joseph Chanc577b8a2006-11-29 15:29:40 +0100777 * mixer widget
778 */
779 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
Josepch Chanf7278fd2007-12-13 16:40:40 +0100780 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
781 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
782 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
783 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
784 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
Joseph Chanc577b8a2006-11-29 15:29:40 +0100785
786 /*
787 * Set up output mixers (0x19 - 0x1b)
788 */
789 /* set vol=0 to output mixers */
790 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
791 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
792 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
793
794 /* Setup default input to PW4 */
795 {0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
Joseph Chanc577b8a2006-11-29 15:29:40 +0100796 /* PW9 Output enable */
797 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100798 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100799};
800
801static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
802 struct hda_codec *codec,
803 struct snd_pcm_substream *substream)
804{
805 struct via_spec *spec = codec->spec;
Lydia Wang17314372009-10-10 19:07:37 +0800806 int idle = substream->pstr->substream_opened == 1
807 && substream->ref_count == 0;
808
809 analog_low_current_mode(codec, idle);
Takashi Iwai9a081602008-02-12 18:37:26 +0100810 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
811 hinfo);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100812}
813
814static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
815 struct hda_codec *codec,
816 unsigned int stream_tag,
817 unsigned int format,
818 struct snd_pcm_substream *substream)
819{
820 struct via_spec *spec = codec->spec;
821 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
822 stream_tag, format, substream);
823}
824
825static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
826 struct hda_codec *codec,
827 struct snd_pcm_substream *substream)
828{
829 struct via_spec *spec = codec->spec;
830 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
831}
832
Harald Welte0aa62ae2008-09-09 15:58:27 +0800833
834static void playback_multi_pcm_prep_0(struct hda_codec *codec,
835 unsigned int stream_tag,
836 unsigned int format,
837 struct snd_pcm_substream *substream)
838{
839 struct via_spec *spec = codec->spec;
840 struct hda_multi_out *mout = &spec->multiout;
841 hda_nid_t *nids = mout->dac_nids;
842 int chs = substream->runtime->channels;
843 int i;
844
845 mutex_lock(&codec->spdif_mutex);
846 if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
847 if (chs == 2 &&
848 snd_hda_is_supported_format(codec, mout->dig_out_nid,
849 format) &&
850 !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
851 mout->dig_out_used = HDA_DIG_ANALOG_DUP;
852 /* turn off SPDIF once; otherwise the IEC958 bits won't
853 * be updated */
854 if (codec->spdif_ctls & AC_DIG1_ENABLE)
855 snd_hda_codec_write(codec, mout->dig_out_nid, 0,
856 AC_VERB_SET_DIGI_CONVERT_1,
857 codec->spdif_ctls &
858 ~AC_DIG1_ENABLE & 0xff);
859 snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
860 stream_tag, 0, format);
861 /* turn on again (if needed) */
862 if (codec->spdif_ctls & AC_DIG1_ENABLE)
863 snd_hda_codec_write(codec, mout->dig_out_nid, 0,
864 AC_VERB_SET_DIGI_CONVERT_1,
865 codec->spdif_ctls & 0xff);
866 } else {
867 mout->dig_out_used = 0;
868 snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
869 0, 0, 0);
870 }
871 }
872 mutex_unlock(&codec->spdif_mutex);
873
874 /* front */
875 snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
876 0, format);
877
878 if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
879 !spec->hp_independent_mode)
880 /* headphone out will just decode front left/right (stereo) */
881 snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
882 0, format);
883
884 /* extra outputs copied from front */
885 for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
886 if (mout->extra_out_nid[i])
887 snd_hda_codec_setup_stream(codec,
888 mout->extra_out_nid[i],
889 stream_tag, 0, format);
890
891 /* surrounds */
892 for (i = 1; i < mout->num_dacs; i++) {
893 if (chs >= (i + 1) * 2) /* independent out */
894 snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
895 i * 2, format);
896 else /* copy front */
897 snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
898 0, format);
899 }
900}
901
902static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
903 struct hda_codec *codec,
904 unsigned int stream_tag,
905 unsigned int format,
906 struct snd_pcm_substream *substream)
907{
908 struct via_spec *spec = codec->spec;
909 struct hda_multi_out *mout = &spec->multiout;
910 hda_nid_t *nids = mout->dac_nids;
911
912 if (substream->number == 0)
913 playback_multi_pcm_prep_0(codec, stream_tag, format,
914 substream);
915 else {
916 if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
917 spec->hp_independent_mode)
918 snd_hda_codec_setup_stream(codec, mout->hp_nid,
919 stream_tag, 0, format);
920 }
921
922 return 0;
923}
924
925static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
926 struct hda_codec *codec,
927 struct snd_pcm_substream *substream)
928{
929 struct via_spec *spec = codec->spec;
930 struct hda_multi_out *mout = &spec->multiout;
931 hda_nid_t *nids = mout->dac_nids;
932 int i;
933
934 if (substream->number == 0) {
935 for (i = 0; i < mout->num_dacs; i++)
936 snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
937
938 if (mout->hp_nid && !spec->hp_independent_mode)
939 snd_hda_codec_setup_stream(codec, mout->hp_nid,
940 0, 0, 0);
941
942 for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
943 if (mout->extra_out_nid[i])
944 snd_hda_codec_setup_stream(codec,
945 mout->extra_out_nid[i],
946 0, 0, 0);
947 mutex_lock(&codec->spdif_mutex);
948 if (mout->dig_out_nid &&
949 mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
950 snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
951 0, 0, 0);
952 mout->dig_out_used = 0;
953 }
954 mutex_unlock(&codec->spdif_mutex);
955 } else {
956 if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
957 spec->hp_independent_mode)
958 snd_hda_codec_setup_stream(codec, mout->hp_nid,
959 0, 0, 0);
960 }
961
962 return 0;
963}
964
Joseph Chanc577b8a2006-11-29 15:29:40 +0100965/*
966 * Digital out
967 */
968static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
969 struct hda_codec *codec,
970 struct snd_pcm_substream *substream)
971{
972 struct via_spec *spec = codec->spec;
973 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
974}
975
976static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
977 struct hda_codec *codec,
978 struct snd_pcm_substream *substream)
979{
980 struct via_spec *spec = codec->spec;
981 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
982}
983
Harald Welte5691ec72008-09-15 22:42:26 +0800984static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +0800985 struct hda_codec *codec,
986 unsigned int stream_tag,
987 unsigned int format,
988 struct snd_pcm_substream *substream)
989{
990 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +0200991 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
992 stream_tag, format, substream);
993}
Harald Welte5691ec72008-09-15 22:42:26 +0800994
Takashi Iwai9da29272009-05-07 16:31:14 +0200995static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
996 struct hda_codec *codec,
997 struct snd_pcm_substream *substream)
998{
999 struct via_spec *spec = codec->spec;
1000 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001001 return 0;
1002}
1003
Joseph Chanc577b8a2006-11-29 15:29:40 +01001004/*
1005 * Analog capture
1006 */
1007static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1008 struct hda_codec *codec,
1009 unsigned int stream_tag,
1010 unsigned int format,
1011 struct snd_pcm_substream *substream)
1012{
1013 struct via_spec *spec = codec->spec;
1014
1015 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1016 stream_tag, 0, format);
1017 return 0;
1018}
1019
1020static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1021 struct hda_codec *codec,
1022 struct snd_pcm_substream *substream)
1023{
1024 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001025 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001026 return 0;
1027}
1028
1029static struct hda_pcm_stream vt1708_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08001030 .substreams = 2,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001031 .channels_min = 2,
1032 .channels_max = 8,
1033 .nid = 0x10, /* NID to query formats and rates */
1034 .ops = {
1035 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001036 .prepare = via_playback_multi_pcm_prepare,
1037 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001038 },
1039};
1040
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001041static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
1042 .substreams = 1,
1043 .channels_min = 2,
1044 .channels_max = 8,
1045 .nid = 0x10, /* NID to query formats and rates */
1046 /* We got noisy outputs on the right channel on VT1708 when
1047 * 24bit samples are used. Until any workaround is found,
1048 * disable the 24bit format, so far.
1049 */
1050 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1051 .ops = {
1052 .open = via_playback_pcm_open,
1053 .prepare = via_playback_pcm_prepare,
1054 .cleanup = via_playback_pcm_cleanup
1055 },
1056};
1057
Joseph Chanc577b8a2006-11-29 15:29:40 +01001058static struct hda_pcm_stream vt1708_pcm_analog_capture = {
1059 .substreams = 2,
1060 .channels_min = 2,
1061 .channels_max = 2,
1062 .nid = 0x15, /* NID to query formats and rates */
1063 .ops = {
1064 .prepare = via_capture_pcm_prepare,
1065 .cleanup = via_capture_pcm_cleanup
1066 },
1067};
1068
1069static struct hda_pcm_stream vt1708_pcm_digital_playback = {
1070 .substreams = 1,
1071 .channels_min = 2,
1072 .channels_max = 2,
1073 /* NID is set in via_build_pcms */
1074 .ops = {
1075 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001076 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001077 .prepare = via_dig_playback_pcm_prepare,
1078 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001079 },
1080};
1081
1082static struct hda_pcm_stream vt1708_pcm_digital_capture = {
1083 .substreams = 1,
1084 .channels_min = 2,
1085 .channels_max = 2,
1086};
1087
1088static int via_build_controls(struct hda_codec *codec)
1089{
1090 struct via_spec *spec = codec->spec;
1091 int err;
1092 int i;
1093
1094 for (i = 0; i < spec->num_mixers; i++) {
1095 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1096 if (err < 0)
1097 return err;
1098 }
1099
1100 if (spec->multiout.dig_out_nid) {
1101 err = snd_hda_create_spdif_out_ctls(codec,
1102 spec->multiout.dig_out_nid);
1103 if (err < 0)
1104 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001105 err = snd_hda_create_spdif_share_sw(codec,
1106 &spec->multiout);
1107 if (err < 0)
1108 return err;
1109 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001110 }
1111 if (spec->dig_in_nid) {
1112 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1113 if (err < 0)
1114 return err;
1115 }
Lydia Wang17314372009-10-10 19:07:37 +08001116
1117 /* init power states */
1118 set_jack_power_state(codec);
1119 analog_low_current_mode(codec, 1);
1120
Takashi Iwai603c4012008-07-30 15:01:44 +02001121 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001122 return 0;
1123}
1124
1125static int via_build_pcms(struct hda_codec *codec)
1126{
1127 struct via_spec *spec = codec->spec;
1128 struct hda_pcm *info = spec->pcm_rec;
1129
1130 codec->num_pcms = 1;
1131 codec->pcm_info = info;
1132
1133 info->name = spec->stream_name_analog;
1134 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
1135 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
1136 info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1137 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1138
1139 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1140 spec->multiout.max_channels;
1141
1142 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1143 codec->num_pcms++;
1144 info++;
1145 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001146 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001147 if (spec->multiout.dig_out_nid) {
1148 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1149 *(spec->stream_digital_playback);
1150 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1151 spec->multiout.dig_out_nid;
1152 }
1153 if (spec->dig_in_nid) {
1154 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1155 *(spec->stream_digital_capture);
1156 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1157 spec->dig_in_nid;
1158 }
1159 }
1160
1161 return 0;
1162}
1163
1164static void via_free(struct hda_codec *codec)
1165{
1166 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001167
1168 if (!spec)
1169 return;
1170
Takashi Iwai603c4012008-07-30 15:01:44 +02001171 via_free_kctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001172 kfree(codec->spec);
1173}
1174
Harald Welte69e52a82008-09-09 15:57:32 +08001175/* mute internal speaker if HP is plugged */
1176static void via_hp_automute(struct hda_codec *codec)
1177{
1178 unsigned int present;
1179 struct via_spec *spec = codec->spec;
1180
1181 present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
1182 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1183 snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
1184 HDA_OUTPUT, 0, HDA_AMP_MUTE,
1185 present ? HDA_AMP_MUTE : 0);
1186}
1187
1188static void via_gpio_control(struct hda_codec *codec)
1189{
1190 unsigned int gpio_data;
1191 unsigned int vol_counter;
1192 unsigned int vol;
1193 unsigned int master_vol;
1194
1195 struct via_spec *spec = codec->spec;
1196
1197 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1198 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1199
1200 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1201 0xF84, 0) & 0x3F0000) >> 16;
1202
1203 vol = vol_counter & 0x1F;
1204 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1205 AC_VERB_GET_AMP_GAIN_MUTE,
1206 AC_AMP_GET_INPUT);
1207
1208 if (gpio_data == 0x02) {
1209 /* unmute line out */
1210 snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
1211 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
1212
1213 if (vol_counter & 0x20) {
1214 /* decrease volume */
1215 if (vol > master_vol)
1216 vol = master_vol;
1217 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1218 0, HDA_AMP_VOLMASK,
1219 master_vol-vol);
1220 } else {
1221 /* increase volume */
1222 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1223 HDA_AMP_VOLMASK,
1224 ((master_vol+vol) > 0x2A) ? 0x2A :
1225 (master_vol+vol));
1226 }
1227 } else if (!(gpio_data & 0x02)) {
1228 /* mute line out */
1229 snd_hda_codec_amp_stereo(codec,
1230 spec->autocfg.line_out_pins[0],
1231 HDA_OUTPUT, 0, HDA_AMP_MUTE,
1232 HDA_AMP_MUTE);
1233 }
1234}
1235
1236/* unsolicited event for jack sensing */
1237static void via_unsol_event(struct hda_codec *codec,
1238 unsigned int res)
1239{
1240 res >>= 26;
1241 if (res == VIA_HP_EVENT)
1242 via_hp_automute(codec);
1243 else if (res == VIA_GPIO_EVENT)
1244 via_gpio_control(codec);
1245}
1246
Joseph Chanc577b8a2006-11-29 15:29:40 +01001247static int via_init(struct hda_codec *codec)
1248{
1249 struct via_spec *spec = codec->spec;
Harald Welte69e52a82008-09-09 15:57:32 +08001250 int i;
1251 for (i = 0; i < spec->num_iverbs; i++)
1252 snd_hda_sequence_write(codec, spec->init_verbs[i]);
1253
Lydia Wang518bf3b2009-10-10 19:07:29 +08001254 spec->codec_type = get_codec_type(codec);
1255 if (spec->codec_type == VT1708BCE)
1256 spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost
1257 same */
Josepch Chanf7278fd2007-12-13 16:40:40 +01001258 /* Lydia Add for EAPD enable */
1259 if (!spec->dig_in_nid) { /* No Digital In connection */
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02001260 if (spec->dig_in_pin) {
1261 snd_hda_codec_write(codec, spec->dig_in_pin, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001262 AC_VERB_SET_PIN_WIDGET_CONTROL,
Takashi Iwai12b74c82008-01-15 12:39:38 +01001263 PIN_OUT);
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02001264 snd_hda_codec_write(codec, spec->dig_in_pin, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001265 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
1266 }
Takashi Iwai12b74c82008-01-15 12:39:38 +01001267 } else /* enable SPDIF-input pin */
1268 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
1269 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
Josepch Chanf7278fd2007-12-13 16:40:40 +01001270
Takashi Iwai9da29272009-05-07 16:31:14 +02001271 /* assign slave outs */
1272 if (spec->slave_dig_outs[0])
1273 codec->slave_dig_outs = spec->slave_dig_outs;
Harald Welte5691ec72008-09-15 22:42:26 +08001274
Joseph Chanc577b8a2006-11-29 15:29:40 +01001275 return 0;
1276}
1277
Takashi Iwaicb53c622007-08-10 17:21:45 +02001278#ifdef CONFIG_SND_HDA_POWER_SAVE
1279static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1280{
1281 struct via_spec *spec = codec->spec;
1282 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1283}
1284#endif
1285
Joseph Chanc577b8a2006-11-29 15:29:40 +01001286/*
1287 */
1288static struct hda_codec_ops via_patch_ops = {
1289 .build_controls = via_build_controls,
1290 .build_pcms = via_build_pcms,
1291 .init = via_init,
1292 .free = via_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +02001293#ifdef CONFIG_SND_HDA_POWER_SAVE
1294 .check_power_status = via_check_power_status,
1295#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001296};
1297
1298/* fill in the dac_nids table from the parsed pin configuration */
1299static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
1300 const struct auto_pin_cfg *cfg)
1301{
1302 int i;
1303 hda_nid_t nid;
1304
1305 spec->multiout.num_dacs = cfg->line_outs;
1306
1307 spec->multiout.dac_nids = spec->private_dac_nids;
1308
1309 for(i = 0; i < 4; i++) {
1310 nid = cfg->line_out_pins[i];
1311 if (nid) {
1312 /* config dac list */
1313 switch (i) {
1314 case AUTO_SEQ_FRONT:
1315 spec->multiout.dac_nids[i] = 0x10;
1316 break;
1317 case AUTO_SEQ_CENLFE:
1318 spec->multiout.dac_nids[i] = 0x12;
1319 break;
1320 case AUTO_SEQ_SURROUND:
Harald Weltefb4cb772008-09-09 15:53:36 +08001321 spec->multiout.dac_nids[i] = 0x11;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001322 break;
1323 case AUTO_SEQ_SIDE:
Harald Weltefb4cb772008-09-09 15:53:36 +08001324 spec->multiout.dac_nids[i] = 0x13;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001325 break;
1326 }
1327 }
1328 }
1329
1330 return 0;
1331}
1332
1333/* add playback controls from the parsed DAC table */
1334static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
1335 const struct auto_pin_cfg *cfg)
1336{
1337 char name[32];
1338 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
1339 hda_nid_t nid, nid_vol = 0;
1340 int i, err;
1341
1342 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
1343 nid = cfg->line_out_pins[i];
1344
1345 if (!nid)
1346 continue;
1347
1348 if (i != AUTO_SEQ_FRONT)
Harald Weltefb4cb772008-09-09 15:53:36 +08001349 nid_vol = 0x18 + i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001350
1351 if (i == AUTO_SEQ_CENLFE) {
1352 /* Center/LFE */
1353 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001354 "Center Playback Volume",
1355 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
1356 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001357 if (err < 0)
1358 return err;
1359 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1360 "LFE Playback Volume",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001361 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
1362 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001363 if (err < 0)
1364 return err;
1365 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1366 "Center Playback Switch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001367 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
1368 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001369 if (err < 0)
1370 return err;
1371 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1372 "LFE Playback Switch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001373 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
1374 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001375 if (err < 0)
1376 return err;
1377 } else if (i == AUTO_SEQ_FRONT){
1378 /* add control to mixer index 0 */
1379 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1380 "Master Front Playback Volume",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001381 HDA_COMPOSE_AMP_VAL(0x17, 3, 0,
1382 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001383 if (err < 0)
1384 return err;
1385 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1386 "Master Front Playback Switch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001387 HDA_COMPOSE_AMP_VAL(0x17, 3, 0,
1388 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001389 if (err < 0)
1390 return err;
1391
1392 /* add control to PW3 */
1393 sprintf(name, "%s Playback Volume", chname[i]);
1394 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001395 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1396 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001397 if (err < 0)
1398 return err;
1399 sprintf(name, "%s Playback Switch", chname[i]);
1400 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001401 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1402 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001403 if (err < 0)
1404 return err;
1405 } else {
1406 sprintf(name, "%s Playback Volume", chname[i]);
1407 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001408 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1409 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001410 if (err < 0)
1411 return err;
1412 sprintf(name, "%s Playback Switch", chname[i]);
1413 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001414 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1415 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001416 if (err < 0)
1417 return err;
1418 }
1419 }
1420
1421 return 0;
1422}
1423
Harald Welte0aa62ae2008-09-09 15:58:27 +08001424static void create_hp_imux(struct via_spec *spec)
1425{
1426 int i;
1427 struct hda_input_mux *imux = &spec->private_imux[1];
1428 static const char *texts[] = { "OFF", "ON", NULL};
1429
1430 /* for hp mode select */
1431 i = 0;
1432 while (texts[i] != NULL) {
1433 imux->items[imux->num_items].label = texts[i];
1434 imux->items[imux->num_items].index = i;
1435 imux->num_items++;
1436 i++;
1437 }
1438
1439 spec->hp_mux = &spec->private_imux[1];
1440}
1441
Joseph Chanc577b8a2006-11-29 15:29:40 +01001442static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
1443{
1444 int err;
1445
1446 if (!pin)
1447 return 0;
1448
1449 spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
1450
1451 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1452 "Headphone Playback Volume",
1453 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
1454 if (err < 0)
1455 return err;
1456 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1457 "Headphone Playback Switch",
1458 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
1459 if (err < 0)
1460 return err;
1461
Harald Welte0aa62ae2008-09-09 15:58:27 +08001462 create_hp_imux(spec);
1463
Joseph Chanc577b8a2006-11-29 15:29:40 +01001464 return 0;
1465}
1466
1467/* create playback/capture controls for input pins */
1468static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
1469 const struct auto_pin_cfg *cfg)
1470{
1471 static char *labels[] = {
1472 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
1473 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08001474 struct hda_input_mux *imux = &spec->private_imux[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001475 int i, err, idx = 0;
1476
1477 /* for internal loopback recording select */
1478 imux->items[imux->num_items].label = "Stereo Mixer";
1479 imux->items[imux->num_items].index = idx;
1480 imux->num_items++;
1481
1482 for (i = 0; i < AUTO_PIN_LAST; i++) {
1483 if (!cfg->input_pins[i])
1484 continue;
1485
1486 switch (cfg->input_pins[i]) {
1487 case 0x1d: /* Mic */
1488 idx = 2;
1489 break;
1490
1491 case 0x1e: /* Line In */
1492 idx = 3;
1493 break;
1494
1495 case 0x21: /* Front Mic */
1496 idx = 4;
1497 break;
1498
1499 case 0x24: /* CD */
1500 idx = 1;
1501 break;
1502 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08001503 err = via_new_analog_input(spec, labels[i], idx, 0x17);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001504 if (err < 0)
1505 return err;
1506 imux->items[imux->num_items].label = labels[i];
1507 imux->items[imux->num_items].index = idx;
1508 imux->num_items++;
1509 }
1510 return 0;
1511}
1512
Takashi Iwaicb53c622007-08-10 17:21:45 +02001513#ifdef CONFIG_SND_HDA_POWER_SAVE
1514static struct hda_amp_list vt1708_loopbacks[] = {
1515 { 0x17, HDA_INPUT, 1 },
1516 { 0x17, HDA_INPUT, 2 },
1517 { 0x17, HDA_INPUT, 3 },
1518 { 0x17, HDA_INPUT, 4 },
1519 { } /* end */
1520};
1521#endif
1522
Harald Welte76d9b0d2008-09-09 15:50:37 +08001523static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
1524{
1525 unsigned int def_conf;
1526 unsigned char seqassoc;
1527
Takashi Iwai2f334f92009-02-20 14:37:42 +01001528 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08001529 seqassoc = (unsigned char) get_defcfg_association(def_conf);
1530 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
1531 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) {
1532 if (seqassoc == 0xff) {
1533 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
Takashi Iwai2f334f92009-02-20 14:37:42 +01001534 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08001535 }
1536 }
1537
1538 return;
1539}
1540
Joseph Chanc577b8a2006-11-29 15:29:40 +01001541static int vt1708_parse_auto_config(struct hda_codec *codec)
1542{
1543 struct via_spec *spec = codec->spec;
1544 int err;
1545
Harald Welte76d9b0d2008-09-09 15:50:37 +08001546 /* Add HP and CD pin config connect bit re-config action */
1547 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
1548 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
1549
Joseph Chanc577b8a2006-11-29 15:29:40 +01001550 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
1551 if (err < 0)
1552 return err;
1553 err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
1554 if (err < 0)
1555 return err;
1556 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
1557 return 0; /* can't find valid BIOS pin config */
1558
1559 err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
1560 if (err < 0)
1561 return err;
1562 err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
1563 if (err < 0)
1564 return err;
1565 err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
1566 if (err < 0)
1567 return err;
1568
1569 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
1570
Takashi Iwai0852d7a2009-02-11 11:35:15 +01001571 if (spec->autocfg.dig_outs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001572 spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02001573 spec->dig_in_pin = VT1708_DIGIN_PIN;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001574 if (spec->autocfg.dig_in_pin)
1575 spec->dig_in_nid = VT1708_DIGIN_NID;
1576
Takashi Iwai603c4012008-07-30 15:01:44 +02001577 if (spec->kctls.list)
1578 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001579
Harald Welte69e52a82008-09-09 15:57:32 +08001580 spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001581
Harald Welte0aa62ae2008-09-09 15:58:27 +08001582 spec->input_mux = &spec->private_imux[0];
1583
Harald Weltef8fdd492008-09-15 22:41:31 +08001584 if (spec->hp_mux)
1585 spec->mixers[spec->num_mixers++] = via_hp_mixer;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001586
1587 return 1;
1588}
1589
1590/* init callback for auto-configuration model -- overriding the default init */
1591static int via_auto_init(struct hda_codec *codec)
1592{
1593 via_init(codec);
1594 via_auto_init_multi_out(codec);
1595 via_auto_init_hp_out(codec);
1596 via_auto_init_analog_input(codec);
1597 return 0;
1598}
1599
Takashi Iwai337b9d02009-07-07 18:18:59 +02001600static int get_mux_nids(struct hda_codec *codec)
1601{
1602 struct via_spec *spec = codec->spec;
1603 hda_nid_t nid, conn[8];
1604 unsigned int type;
1605 int i, n;
1606
1607 for (i = 0; i < spec->num_adc_nids; i++) {
1608 nid = spec->adc_nids[i];
1609 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02001610 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02001611 if (type == AC_WID_PIN)
1612 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02001613 n = snd_hda_get_connections(codec, nid, conn,
1614 ARRAY_SIZE(conn));
1615 if (n <= 0)
1616 break;
1617 if (n > 1) {
1618 spec->mux_nids[i] = nid;
1619 break;
1620 }
1621 nid = conn[0];
1622 }
1623 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02001624 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02001625}
1626
Joseph Chanc577b8a2006-11-29 15:29:40 +01001627static int patch_vt1708(struct hda_codec *codec)
1628{
1629 struct via_spec *spec;
1630 int err;
1631
1632 /* create a codec specific record */
Harald Welteeb14a462008-09-09 15:40:38 +08001633 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001634 if (spec == NULL)
1635 return -ENOMEM;
1636
1637 codec->spec = spec;
1638
1639 /* automatic parse from the BIOS config */
1640 err = vt1708_parse_auto_config(codec);
1641 if (err < 0) {
1642 via_free(codec);
1643 return err;
1644 } else if (!err) {
1645 printk(KERN_INFO "hda_codec: Cannot set up configuration "
1646 "from BIOS. Using genenic mode...\n");
1647 }
1648
1649
1650 spec->stream_name_analog = "VT1708 Analog";
1651 spec->stream_analog_playback = &vt1708_pcm_analog_playback;
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001652 /* disable 32bit format on VT1708 */
1653 if (codec->vendor_id == 0x11061708)
1654 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001655 spec->stream_analog_capture = &vt1708_pcm_analog_capture;
1656
1657 spec->stream_name_digital = "VT1708 Digital";
1658 spec->stream_digital_playback = &vt1708_pcm_digital_playback;
1659 spec->stream_digital_capture = &vt1708_pcm_digital_capture;
1660
1661
1662 if (!spec->adc_nids && spec->input_mux) {
1663 spec->adc_nids = vt1708_adc_nids;
1664 spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
Takashi Iwai0f67a612009-08-31 08:12:29 +02001665 get_mux_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001666 spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
1667 spec->num_mixers++;
1668 }
1669
1670 codec->patch_ops = via_patch_ops;
1671
1672 codec->patch_ops.init = via_auto_init;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001673#ifdef CONFIG_SND_HDA_POWER_SAVE
1674 spec->loopback.amplist = vt1708_loopbacks;
1675#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001676
1677 return 0;
1678}
1679
1680/* capture mixer elements */
1681static struct snd_kcontrol_new vt1709_capture_mixer[] = {
1682 HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
1683 HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
1684 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
1685 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
1686 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
1687 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
1688 {
1689 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1690 /* The multiple "Capture Source" controls confuse alsamixer
1691 * So call somewhat different..
Joseph Chanc577b8a2006-11-29 15:29:40 +01001692 */
1693 /* .name = "Capture Source", */
1694 .name = "Input Source",
1695 .count = 1,
1696 .info = via_mux_enum_info,
1697 .get = via_mux_enum_get,
1698 .put = via_mux_enum_put,
1699 },
1700 { } /* end */
1701};
1702
Harald Welte69e52a82008-09-09 15:57:32 +08001703static struct hda_verb vt1709_uniwill_init_verbs[] = {
1704 {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
1705 { }
1706};
1707
Joseph Chanc577b8a2006-11-29 15:29:40 +01001708/*
1709 * generic initialization of ADC, input mixers and output mixers
1710 */
1711static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
1712 /*
1713 * Unmute ADC0-2 and set the default input to mic-in
1714 */
1715 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1716 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1717 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1718
1719
Josepch Chanf7278fd2007-12-13 16:40:40 +01001720 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
Joseph Chanc577b8a2006-11-29 15:29:40 +01001721 * mixer widget
1722 */
1723 /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
Josepch Chanf7278fd2007-12-13 16:40:40 +01001724 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1725 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1726 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
1727 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
1728 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
Joseph Chanc577b8a2006-11-29 15:29:40 +01001729
1730 /*
1731 * Set up output selector (0x1a, 0x1b, 0x29)
1732 */
1733 /* set vol=0 to output mixers */
1734 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1735 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1736 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1737
1738 /*
1739 * Unmute PW3 and PW4
1740 */
1741 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1742 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1743
1744 /* Set input of PW4 as AOW4 */
1745 {0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
Joseph Chanc577b8a2006-11-29 15:29:40 +01001746 /* PW9 Output enable */
1747 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
1748 { }
1749};
1750
1751static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
1752 .substreams = 1,
1753 .channels_min = 2,
1754 .channels_max = 10,
1755 .nid = 0x10, /* NID to query formats and rates */
1756 .ops = {
1757 .open = via_playback_pcm_open,
1758 .prepare = via_playback_pcm_prepare,
1759 .cleanup = via_playback_pcm_cleanup
1760 },
1761};
1762
1763static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
1764 .substreams = 1,
1765 .channels_min = 2,
1766 .channels_max = 6,
1767 .nid = 0x10, /* NID to query formats and rates */
1768 .ops = {
1769 .open = via_playback_pcm_open,
1770 .prepare = via_playback_pcm_prepare,
1771 .cleanup = via_playback_pcm_cleanup
1772 },
1773};
1774
1775static struct hda_pcm_stream vt1709_pcm_analog_capture = {
1776 .substreams = 2,
1777 .channels_min = 2,
1778 .channels_max = 2,
1779 .nid = 0x14, /* NID to query formats and rates */
1780 .ops = {
1781 .prepare = via_capture_pcm_prepare,
1782 .cleanup = via_capture_pcm_cleanup
1783 },
1784};
1785
1786static struct hda_pcm_stream vt1709_pcm_digital_playback = {
1787 .substreams = 1,
1788 .channels_min = 2,
1789 .channels_max = 2,
1790 /* NID is set in via_build_pcms */
1791 .ops = {
1792 .open = via_dig_playback_pcm_open,
1793 .close = via_dig_playback_pcm_close
1794 },
1795};
1796
1797static struct hda_pcm_stream vt1709_pcm_digital_capture = {
1798 .substreams = 1,
1799 .channels_min = 2,
1800 .channels_max = 2,
1801};
1802
1803static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
1804 const struct auto_pin_cfg *cfg)
1805{
1806 int i;
1807 hda_nid_t nid;
1808
1809 if (cfg->line_outs == 4) /* 10 channels */
1810 spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
1811 else if (cfg->line_outs == 3) /* 6 channels */
1812 spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
1813
1814 spec->multiout.dac_nids = spec->private_dac_nids;
1815
1816 if (cfg->line_outs == 4) { /* 10 channels */
1817 for (i = 0; i < cfg->line_outs; i++) {
1818 nid = cfg->line_out_pins[i];
1819 if (nid) {
1820 /* config dac list */
1821 switch (i) {
1822 case AUTO_SEQ_FRONT:
1823 /* AOW0 */
1824 spec->multiout.dac_nids[i] = 0x10;
1825 break;
1826 case AUTO_SEQ_CENLFE:
1827 /* AOW2 */
1828 spec->multiout.dac_nids[i] = 0x12;
1829 break;
1830 case AUTO_SEQ_SURROUND:
1831 /* AOW3 */
Harald Weltefb4cb772008-09-09 15:53:36 +08001832 spec->multiout.dac_nids[i] = 0x11;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001833 break;
1834 case AUTO_SEQ_SIDE:
1835 /* AOW1 */
Harald Weltefb4cb772008-09-09 15:53:36 +08001836 spec->multiout.dac_nids[i] = 0x27;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001837 break;
1838 default:
1839 break;
1840 }
1841 }
1842 }
1843 spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
1844
1845 } else if (cfg->line_outs == 3) { /* 6 channels */
1846 for(i = 0; i < cfg->line_outs; i++) {
1847 nid = cfg->line_out_pins[i];
1848 if (nid) {
1849 /* config dac list */
1850 switch(i) {
1851 case AUTO_SEQ_FRONT:
1852 /* AOW0 */
1853 spec->multiout.dac_nids[i] = 0x10;
1854 break;
1855 case AUTO_SEQ_CENLFE:
1856 /* AOW2 */
1857 spec->multiout.dac_nids[i] = 0x12;
1858 break;
1859 case AUTO_SEQ_SURROUND:
1860 /* AOW1 */
1861 spec->multiout.dac_nids[i] = 0x11;
1862 break;
1863 default:
1864 break;
1865 }
1866 }
1867 }
1868 }
1869
1870 return 0;
1871}
1872
1873/* add playback controls from the parsed DAC table */
1874static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
1875 const struct auto_pin_cfg *cfg)
1876{
1877 char name[32];
1878 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
1879 hda_nid_t nid = 0;
1880 int i, err;
1881
1882 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
1883 nid = cfg->line_out_pins[i];
1884
1885 if (!nid)
1886 continue;
1887
1888 if (i == AUTO_SEQ_CENLFE) {
1889 /* Center/LFE */
1890 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1891 "Center Playback Volume",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001892 HDA_COMPOSE_AMP_VAL(0x1b, 1, 0,
1893 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001894 if (err < 0)
1895 return err;
1896 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1897 "LFE Playback Volume",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001898 HDA_COMPOSE_AMP_VAL(0x1b, 2, 0,
1899 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001900 if (err < 0)
1901 return err;
1902 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1903 "Center Playback Switch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001904 HDA_COMPOSE_AMP_VAL(0x1b, 1, 0,
1905 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001906 if (err < 0)
1907 return err;
1908 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1909 "LFE Playback Switch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001910 HDA_COMPOSE_AMP_VAL(0x1b, 2, 0,
1911 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001912 if (err < 0)
1913 return err;
1914 } else if (i == AUTO_SEQ_FRONT){
1915 /* add control to mixer index 0 */
1916 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1917 "Master Front Playback Volume",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001918 HDA_COMPOSE_AMP_VAL(0x18, 3, 0,
1919 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001920 if (err < 0)
1921 return err;
1922 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1923 "Master Front Playback Switch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001924 HDA_COMPOSE_AMP_VAL(0x18, 3, 0,
1925 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001926 if (err < 0)
1927 return err;
1928
1929 /* add control to PW3 */
1930 sprintf(name, "%s Playback Volume", chname[i]);
1931 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001932 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1933 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001934 if (err < 0)
1935 return err;
1936 sprintf(name, "%s Playback Switch", chname[i]);
1937 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001938 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1939 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001940 if (err < 0)
1941 return err;
1942 } else if (i == AUTO_SEQ_SURROUND) {
1943 sprintf(name, "%s Playback Volume", chname[i]);
1944 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Harald Weltefb4cb772008-09-09 15:53:36 +08001945 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001946 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001947 if (err < 0)
1948 return err;
1949 sprintf(name, "%s Playback Switch", chname[i]);
1950 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Harald Weltefb4cb772008-09-09 15:53:36 +08001951 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001952 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001953 if (err < 0)
1954 return err;
1955 } else if (i == AUTO_SEQ_SIDE) {
1956 sprintf(name, "%s Playback Volume", chname[i]);
1957 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Harald Weltefb4cb772008-09-09 15:53:36 +08001958 HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001959 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001960 if (err < 0)
1961 return err;
1962 sprintf(name, "%s Playback Switch", chname[i]);
1963 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Harald Weltefb4cb772008-09-09 15:53:36 +08001964 HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001965 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001966 if (err < 0)
1967 return err;
1968 }
1969 }
1970
1971 return 0;
1972}
1973
1974static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
1975{
1976 int err;
1977
1978 if (!pin)
1979 return 0;
1980
1981 if (spec->multiout.num_dacs == 5) /* 10 channels */
1982 spec->multiout.hp_nid = VT1709_HP_DAC_NID;
1983 else if (spec->multiout.num_dacs == 3) /* 6 channels */
1984 spec->multiout.hp_nid = 0;
1985
1986 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1987 "Headphone Playback Volume",
1988 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
1989 if (err < 0)
1990 return err;
1991 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1992 "Headphone Playback Switch",
1993 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
1994 if (err < 0)
1995 return err;
1996
1997 return 0;
1998}
1999
2000/* create playback/capture controls for input pins */
2001static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
2002 const struct auto_pin_cfg *cfg)
2003{
2004 static char *labels[] = {
2005 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
2006 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08002007 struct hda_input_mux *imux = &spec->private_imux[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01002008 int i, err, idx = 0;
2009
2010 /* for internal loopback recording select */
2011 imux->items[imux->num_items].label = "Stereo Mixer";
2012 imux->items[imux->num_items].index = idx;
2013 imux->num_items++;
2014
2015 for (i = 0; i < AUTO_PIN_LAST; i++) {
2016 if (!cfg->input_pins[i])
2017 continue;
2018
2019 switch (cfg->input_pins[i]) {
2020 case 0x1d: /* Mic */
2021 idx = 2;
2022 break;
2023
2024 case 0x1e: /* Line In */
2025 idx = 3;
2026 break;
2027
2028 case 0x21: /* Front Mic */
2029 idx = 4;
2030 break;
2031
2032 case 0x23: /* CD */
2033 idx = 1;
2034 break;
2035 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08002036 err = via_new_analog_input(spec, labels[i], idx, 0x18);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002037 if (err < 0)
2038 return err;
2039 imux->items[imux->num_items].label = labels[i];
2040 imux->items[imux->num_items].index = idx;
2041 imux->num_items++;
2042 }
2043 return 0;
2044}
2045
2046static int vt1709_parse_auto_config(struct hda_codec *codec)
2047{
2048 struct via_spec *spec = codec->spec;
2049 int err;
2050
2051 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2052 if (err < 0)
2053 return err;
2054 err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
2055 if (err < 0)
2056 return err;
2057 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2058 return 0; /* can't find valid BIOS pin config */
2059
2060 err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
2061 if (err < 0)
2062 return err;
2063 err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2064 if (err < 0)
2065 return err;
2066 err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg);
2067 if (err < 0)
2068 return err;
2069
2070 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2071
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002072 if (spec->autocfg.dig_outs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002073 spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02002074 spec->dig_in_pin = VT1709_DIGIN_PIN;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002075 if (spec->autocfg.dig_in_pin)
2076 spec->dig_in_nid = VT1709_DIGIN_NID;
2077
Takashi Iwai603c4012008-07-30 15:01:44 +02002078 if (spec->kctls.list)
2079 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002080
Harald Welte0aa62ae2008-09-09 15:58:27 +08002081 spec->input_mux = &spec->private_imux[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01002082
Harald Weltef8fdd492008-09-15 22:41:31 +08002083 if (spec->hp_mux)
2084 spec->mixers[spec->num_mixers++] = via_hp_mixer;
2085
Joseph Chanc577b8a2006-11-29 15:29:40 +01002086 return 1;
2087}
2088
Takashi Iwaicb53c622007-08-10 17:21:45 +02002089#ifdef CONFIG_SND_HDA_POWER_SAVE
2090static struct hda_amp_list vt1709_loopbacks[] = {
2091 { 0x18, HDA_INPUT, 1 },
2092 { 0x18, HDA_INPUT, 2 },
2093 { 0x18, HDA_INPUT, 3 },
2094 { 0x18, HDA_INPUT, 4 },
2095 { } /* end */
2096};
2097#endif
2098
Joseph Chanc577b8a2006-11-29 15:29:40 +01002099static int patch_vt1709_10ch(struct hda_codec *codec)
2100{
2101 struct via_spec *spec;
2102 int err;
2103
2104 /* create a codec specific record */
Harald Welteeb14a462008-09-09 15:40:38 +08002105 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002106 if (spec == NULL)
2107 return -ENOMEM;
2108
2109 codec->spec = spec;
2110
2111 err = vt1709_parse_auto_config(codec);
2112 if (err < 0) {
2113 via_free(codec);
2114 return err;
2115 } else if (!err) {
2116 printk(KERN_INFO "hda_codec: Cannot set up configuration. "
2117 "Using genenic mode...\n");
2118 }
2119
Harald Welte69e52a82008-09-09 15:57:32 +08002120 spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
2121 spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002122
2123 spec->stream_name_analog = "VT1709 Analog";
2124 spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
2125 spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2126
2127 spec->stream_name_digital = "VT1709 Digital";
2128 spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2129 spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2130
2131
2132 if (!spec->adc_nids && spec->input_mux) {
2133 spec->adc_nids = vt1709_adc_nids;
2134 spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02002135 get_mux_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002136 spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2137 spec->num_mixers++;
2138 }
2139
2140 codec->patch_ops = via_patch_ops;
2141
2142 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08002143 codec->patch_ops.unsol_event = via_unsol_event;
Takashi Iwaicb53c622007-08-10 17:21:45 +02002144#ifdef CONFIG_SND_HDA_POWER_SAVE
2145 spec->loopback.amplist = vt1709_loopbacks;
2146#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01002147
2148 return 0;
2149}
2150/*
2151 * generic initialization of ADC, input mixers and output mixers
2152 */
2153static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
2154 /*
2155 * Unmute ADC0-2 and set the default input to mic-in
2156 */
2157 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2158 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2159 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2160
2161
2162 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2163 * mixer widget
2164 */
2165 /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2166 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2167 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2168 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2169 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2170 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2171
2172 /*
2173 * Set up output selector (0x1a, 0x1b, 0x29)
2174 */
2175 /* set vol=0 to output mixers */
2176 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2177 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2178 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2179
2180 /*
2181 * Unmute PW3 and PW4
2182 */
2183 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2184 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2185
2186 /* Set input of PW4 as MW0 */
2187 {0x20, AC_VERB_SET_CONNECT_SEL, 0},
Joseph Chanc577b8a2006-11-29 15:29:40 +01002188 /* PW9 Output enable */
2189 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2190 { }
2191};
2192
2193static int patch_vt1709_6ch(struct hda_codec *codec)
2194{
2195 struct via_spec *spec;
2196 int err;
2197
2198 /* create a codec specific record */
Harald Welteeb14a462008-09-09 15:40:38 +08002199 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002200 if (spec == NULL)
2201 return -ENOMEM;
2202
2203 codec->spec = spec;
2204
2205 err = vt1709_parse_auto_config(codec);
2206 if (err < 0) {
2207 via_free(codec);
2208 return err;
2209 } else if (!err) {
2210 printk(KERN_INFO "hda_codec: Cannot set up configuration. "
2211 "Using genenic mode...\n");
2212 }
2213
Harald Welte69e52a82008-09-09 15:57:32 +08002214 spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
2215 spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002216
2217 spec->stream_name_analog = "VT1709 Analog";
2218 spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
2219 spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2220
2221 spec->stream_name_digital = "VT1709 Digital";
2222 spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2223 spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2224
2225
2226 if (!spec->adc_nids && spec->input_mux) {
2227 spec->adc_nids = vt1709_adc_nids;
2228 spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02002229 get_mux_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002230 spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2231 spec->num_mixers++;
2232 }
2233
2234 codec->patch_ops = via_patch_ops;
2235
2236 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08002237 codec->patch_ops.unsol_event = via_unsol_event;
Takashi Iwaicb53c622007-08-10 17:21:45 +02002238#ifdef CONFIG_SND_HDA_POWER_SAVE
2239 spec->loopback.amplist = vt1709_loopbacks;
2240#endif
Josepch Chanf7278fd2007-12-13 16:40:40 +01002241 return 0;
2242}
2243
2244/* capture mixer elements */
2245static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
2246 HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
2247 HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
2248 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
2249 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
2250 {
2251 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2252 /* The multiple "Capture Source" controls confuse alsamixer
2253 * So call somewhat different..
Josepch Chanf7278fd2007-12-13 16:40:40 +01002254 */
2255 /* .name = "Capture Source", */
2256 .name = "Input Source",
2257 .count = 1,
2258 .info = via_mux_enum_info,
2259 .get = via_mux_enum_get,
2260 .put = via_mux_enum_put,
2261 },
2262 { } /* end */
2263};
2264/*
2265 * generic initialization of ADC, input mixers and output mixers
2266 */
2267static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
2268 /*
2269 * Unmute ADC0-1 and set the default input to mic-in
2270 */
2271 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2272 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2273
2274
2275 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2276 * mixer widget
2277 */
2278 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2279 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2280 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2281 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2282 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2283 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2284
2285 /*
2286 * Set up output mixers
2287 */
2288 /* set vol=0 to output mixers */
2289 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2290 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2291 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2292
2293 /* Setup default input to PW4 */
2294 {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1},
2295 /* PW9 Output enable */
2296 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2297 /* PW10 Input enable */
2298 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2299 { }
2300};
2301
2302static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
2303 /*
2304 * Unmute ADC0-1 and set the default input to mic-in
2305 */
2306 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2307 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2308
2309
2310 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2311 * mixer widget
2312 */
2313 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2314 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2315 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2316 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2317 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2318 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2319
2320 /*
2321 * Set up output mixers
2322 */
2323 /* set vol=0 to output mixers */
2324 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2325 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2326 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2327
2328 /* Setup default input of PW4 to MW0 */
2329 {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
2330 /* PW9 Output enable */
2331 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2332 /* PW10 Input enable */
2333 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2334 { }
2335};
2336
Harald Welte69e52a82008-09-09 15:57:32 +08002337static struct hda_verb vt1708B_uniwill_init_verbs[] = {
2338 {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
2339 { }
2340};
2341
Lydia Wang17314372009-10-10 19:07:37 +08002342static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
2343 struct hda_codec *codec,
2344 struct snd_pcm_substream *substream)
2345{
2346 int idle = substream->pstr->substream_opened == 1
2347 && substream->ref_count == 0;
2348
2349 analog_low_current_mode(codec, idle);
2350 return 0;
2351}
2352
Josepch Chanf7278fd2007-12-13 16:40:40 +01002353static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08002354 .substreams = 2,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002355 .channels_min = 2,
2356 .channels_max = 8,
2357 .nid = 0x10, /* NID to query formats and rates */
2358 .ops = {
2359 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08002360 .prepare = via_playback_multi_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08002361 .cleanup = via_playback_multi_pcm_cleanup,
2362 .close = via_pcm_open_close
Josepch Chanf7278fd2007-12-13 16:40:40 +01002363 },
2364};
2365
2366static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08002367 .substreams = 2,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002368 .channels_min = 2,
2369 .channels_max = 4,
2370 .nid = 0x10, /* NID to query formats and rates */
2371 .ops = {
2372 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08002373 .prepare = via_playback_multi_pcm_prepare,
2374 .cleanup = via_playback_multi_pcm_cleanup
Josepch Chanf7278fd2007-12-13 16:40:40 +01002375 },
2376};
2377
2378static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
2379 .substreams = 2,
2380 .channels_min = 2,
2381 .channels_max = 2,
2382 .nid = 0x13, /* NID to query formats and rates */
2383 .ops = {
Lydia Wang17314372009-10-10 19:07:37 +08002384 .open = via_pcm_open_close,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002385 .prepare = via_capture_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08002386 .cleanup = via_capture_pcm_cleanup,
2387 .close = via_pcm_open_close
Josepch Chanf7278fd2007-12-13 16:40:40 +01002388 },
2389};
2390
2391static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
2392 .substreams = 1,
2393 .channels_min = 2,
2394 .channels_max = 2,
2395 /* NID is set in via_build_pcms */
2396 .ops = {
2397 .open = via_dig_playback_pcm_open,
2398 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02002399 .prepare = via_dig_playback_pcm_prepare,
2400 .cleanup = via_dig_playback_pcm_cleanup
Josepch Chanf7278fd2007-12-13 16:40:40 +01002401 },
2402};
2403
2404static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
2405 .substreams = 1,
2406 .channels_min = 2,
2407 .channels_max = 2,
2408};
2409
2410/* fill in the dac_nids table from the parsed pin configuration */
2411static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
2412 const struct auto_pin_cfg *cfg)
2413{
2414 int i;
2415 hda_nid_t nid;
2416
2417 spec->multiout.num_dacs = cfg->line_outs;
2418
2419 spec->multiout.dac_nids = spec->private_dac_nids;
2420
2421 for (i = 0; i < 4; i++) {
2422 nid = cfg->line_out_pins[i];
2423 if (nid) {
2424 /* config dac list */
2425 switch (i) {
2426 case AUTO_SEQ_FRONT:
2427 spec->multiout.dac_nids[i] = 0x10;
2428 break;
2429 case AUTO_SEQ_CENLFE:
2430 spec->multiout.dac_nids[i] = 0x24;
2431 break;
2432 case AUTO_SEQ_SURROUND:
Harald Weltefb4cb772008-09-09 15:53:36 +08002433 spec->multiout.dac_nids[i] = 0x11;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002434 break;
2435 case AUTO_SEQ_SIDE:
Harald Weltefb4cb772008-09-09 15:53:36 +08002436 spec->multiout.dac_nids[i] = 0x25;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002437 break;
2438 }
2439 }
2440 }
2441
2442 return 0;
2443}
2444
2445/* add playback controls from the parsed DAC table */
2446static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
2447 const struct auto_pin_cfg *cfg)
2448{
2449 char name[32];
2450 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
Harald Weltefb4cb772008-09-09 15:53:36 +08002451 hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
Josepch Chanf7278fd2007-12-13 16:40:40 +01002452 hda_nid_t nid, nid_vol = 0;
2453 int i, err;
2454
2455 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2456 nid = cfg->line_out_pins[i];
2457
2458 if (!nid)
2459 continue;
2460
2461 nid_vol = nid_vols[i];
2462
2463 if (i == AUTO_SEQ_CENLFE) {
2464 /* Center/LFE */
2465 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2466 "Center Playback Volume",
2467 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2468 HDA_OUTPUT));
2469 if (err < 0)
2470 return err;
2471 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2472 "LFE Playback Volume",
2473 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2474 HDA_OUTPUT));
2475 if (err < 0)
2476 return err;
2477 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2478 "Center Playback Switch",
2479 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2480 HDA_OUTPUT));
2481 if (err < 0)
2482 return err;
2483 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2484 "LFE Playback Switch",
2485 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2486 HDA_OUTPUT));
2487 if (err < 0)
2488 return err;
2489 } else if (i == AUTO_SEQ_FRONT) {
2490 /* add control to mixer index 0 */
2491 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2492 "Master Front Playback Volume",
2493 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2494 HDA_INPUT));
2495 if (err < 0)
2496 return err;
2497 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2498 "Master Front Playback Switch",
2499 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2500 HDA_INPUT));
2501 if (err < 0)
2502 return err;
2503
2504 /* add control to PW3 */
2505 sprintf(name, "%s Playback Volume", chname[i]);
2506 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2507 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2508 HDA_OUTPUT));
2509 if (err < 0)
2510 return err;
2511 sprintf(name, "%s Playback Switch", chname[i]);
2512 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2513 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2514 HDA_OUTPUT));
2515 if (err < 0)
2516 return err;
2517 } else {
2518 sprintf(name, "%s Playback Volume", chname[i]);
2519 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2520 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2521 HDA_OUTPUT));
2522 if (err < 0)
2523 return err;
2524 sprintf(name, "%s Playback Switch", chname[i]);
2525 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2526 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2527 HDA_OUTPUT));
2528 if (err < 0)
2529 return err;
2530 }
2531 }
2532
2533 return 0;
2534}
2535
2536static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2537{
2538 int err;
2539
2540 if (!pin)
2541 return 0;
2542
2543 spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
2544
2545 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2546 "Headphone Playback Volume",
2547 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2548 if (err < 0)
2549 return err;
2550 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2551 "Headphone Playback Switch",
2552 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2553 if (err < 0)
2554 return err;
2555
Harald Welte0aa62ae2008-09-09 15:58:27 +08002556 create_hp_imux(spec);
2557
Josepch Chanf7278fd2007-12-13 16:40:40 +01002558 return 0;
2559}
2560
2561/* create playback/capture controls for input pins */
2562static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
2563 const struct auto_pin_cfg *cfg)
2564{
2565 static char *labels[] = {
2566 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
2567 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08002568 struct hda_input_mux *imux = &spec->private_imux[0];
Josepch Chanf7278fd2007-12-13 16:40:40 +01002569 int i, err, idx = 0;
2570
2571 /* for internal loopback recording select */
2572 imux->items[imux->num_items].label = "Stereo Mixer";
2573 imux->items[imux->num_items].index = idx;
2574 imux->num_items++;
2575
2576 for (i = 0; i < AUTO_PIN_LAST; i++) {
2577 if (!cfg->input_pins[i])
2578 continue;
2579
2580 switch (cfg->input_pins[i]) {
2581 case 0x1a: /* Mic */
2582 idx = 2;
2583 break;
2584
2585 case 0x1b: /* Line In */
2586 idx = 3;
2587 break;
2588
2589 case 0x1e: /* Front Mic */
2590 idx = 4;
2591 break;
2592
2593 case 0x1f: /* CD */
2594 idx = 1;
2595 break;
2596 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08002597 err = via_new_analog_input(spec, labels[i], idx, 0x16);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002598 if (err < 0)
2599 return err;
2600 imux->items[imux->num_items].label = labels[i];
2601 imux->items[imux->num_items].index = idx;
2602 imux->num_items++;
2603 }
2604 return 0;
2605}
2606
2607static int vt1708B_parse_auto_config(struct hda_codec *codec)
2608{
2609 struct via_spec *spec = codec->spec;
2610 int err;
2611
2612 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2613 if (err < 0)
2614 return err;
2615 err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
2616 if (err < 0)
2617 return err;
2618 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2619 return 0; /* can't find valid BIOS pin config */
2620
2621 err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
2622 if (err < 0)
2623 return err;
2624 err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2625 if (err < 0)
2626 return err;
2627 err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
2628 if (err < 0)
2629 return err;
2630
2631 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2632
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002633 if (spec->autocfg.dig_outs)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002634 spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02002635 spec->dig_in_pin = VT1708B_DIGIN_PIN;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002636 if (spec->autocfg.dig_in_pin)
2637 spec->dig_in_nid = VT1708B_DIGIN_NID;
2638
Takashi Iwai603c4012008-07-30 15:01:44 +02002639 if (spec->kctls.list)
2640 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002641
Harald Welte0aa62ae2008-09-09 15:58:27 +08002642 spec->input_mux = &spec->private_imux[0];
2643
Harald Weltef8fdd492008-09-15 22:41:31 +08002644 if (spec->hp_mux)
2645 spec->mixers[spec->num_mixers++] = via_hp_mixer;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002646
2647 return 1;
2648}
2649
2650#ifdef CONFIG_SND_HDA_POWER_SAVE
2651static struct hda_amp_list vt1708B_loopbacks[] = {
2652 { 0x16, HDA_INPUT, 1 },
2653 { 0x16, HDA_INPUT, 2 },
2654 { 0x16, HDA_INPUT, 3 },
2655 { 0x16, HDA_INPUT, 4 },
2656 { } /* end */
2657};
2658#endif
Lydia Wang518bf3b2009-10-10 19:07:29 +08002659static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002660static int patch_vt1708B_8ch(struct hda_codec *codec)
2661{
2662 struct via_spec *spec;
2663 int err;
2664
Lydia Wang518bf3b2009-10-10 19:07:29 +08002665 if (get_codec_type(codec) == VT1708BCE)
2666 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002667 /* create a codec specific record */
Harald Welteeb14a462008-09-09 15:40:38 +08002668 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002669 if (spec == NULL)
2670 return -ENOMEM;
2671
2672 codec->spec = spec;
2673
2674 /* automatic parse from the BIOS config */
2675 err = vt1708B_parse_auto_config(codec);
2676 if (err < 0) {
2677 via_free(codec);
2678 return err;
2679 } else if (!err) {
2680 printk(KERN_INFO "hda_codec: Cannot set up configuration "
2681 "from BIOS. Using genenic mode...\n");
2682 }
2683
Harald Welte69e52a82008-09-09 15:57:32 +08002684 spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
2685 spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002686
2687 spec->stream_name_analog = "VT1708B Analog";
2688 spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
2689 spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
2690
2691 spec->stream_name_digital = "VT1708B Digital";
2692 spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
2693 spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
2694
2695 if (!spec->adc_nids && spec->input_mux) {
2696 spec->adc_nids = vt1708B_adc_nids;
2697 spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02002698 get_mux_nids(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002699 spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
2700 spec->num_mixers++;
2701 }
2702
2703 codec->patch_ops = via_patch_ops;
2704
2705 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08002706 codec->patch_ops.unsol_event = via_unsol_event;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002707#ifdef CONFIG_SND_HDA_POWER_SAVE
2708 spec->loopback.amplist = vt1708B_loopbacks;
2709#endif
2710
2711 return 0;
2712}
2713
2714static int patch_vt1708B_4ch(struct hda_codec *codec)
2715{
2716 struct via_spec *spec;
2717 int err;
2718
2719 /* create a codec specific record */
Harald Welteeb14a462008-09-09 15:40:38 +08002720 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002721 if (spec == NULL)
2722 return -ENOMEM;
2723
2724 codec->spec = spec;
2725
2726 /* automatic parse from the BIOS config */
2727 err = vt1708B_parse_auto_config(codec);
2728 if (err < 0) {
2729 via_free(codec);
2730 return err;
2731 } else if (!err) {
2732 printk(KERN_INFO "hda_codec: Cannot set up configuration "
2733 "from BIOS. Using genenic mode...\n");
2734 }
2735
Harald Welte69e52a82008-09-09 15:57:32 +08002736 spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
2737 spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002738
2739 spec->stream_name_analog = "VT1708B Analog";
2740 spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
2741 spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
2742
2743 spec->stream_name_digital = "VT1708B Digital";
2744 spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
2745 spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
2746
2747 if (!spec->adc_nids && spec->input_mux) {
2748 spec->adc_nids = vt1708B_adc_nids;
2749 spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02002750 get_mux_nids(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002751 spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
2752 spec->num_mixers++;
2753 }
2754
2755 codec->patch_ops = via_patch_ops;
2756
2757 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08002758 codec->patch_ops.unsol_event = via_unsol_event;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002759#ifdef CONFIG_SND_HDA_POWER_SAVE
2760 spec->loopback.amplist = vt1708B_loopbacks;
2761#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01002762
2763 return 0;
2764}
2765
Harald Welted949cac2008-09-09 15:56:01 +08002766/* Patch for VT1708S */
2767
Harald Welted7426322008-09-15 22:43:23 +08002768/* VT1708S software backdoor based override for buggy hardware micboost
2769 * setting */
2770#define MIC_BOOST_VOLUME(xname, nid) { \
2771 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2772 .name = xname, \
2773 .index = 0, \
2774 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
2775 SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
2776 SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
2777 .info = mic_boost_volume_info, \
2778 .get = snd_hda_mixer_amp_volume_get, \
2779 .put = snd_hda_mixer_amp_volume_put, \
2780 .tlv = { .c = mic_boost_tlv }, \
2781 .private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT) }
2782
Harald Welted949cac2008-09-09 15:56:01 +08002783/* capture mixer elements */
2784static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
2785 HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
2786 HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
2787 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
2788 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
Harald Welted7426322008-09-15 22:43:23 +08002789 MIC_BOOST_VOLUME("Mic Boost Capture Volume", 0x1A),
2790 MIC_BOOST_VOLUME("Front Mic Boost Capture Volume", 0x1E),
Harald Welted949cac2008-09-09 15:56:01 +08002791 {
2792 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2793 /* The multiple "Capture Source" controls confuse alsamixer
2794 * So call somewhat different..
2795 */
2796 /* .name = "Capture Source", */
2797 .name = "Input Source",
2798 .count = 1,
2799 .info = via_mux_enum_info,
2800 .get = via_mux_enum_get,
2801 .put = via_mux_enum_put,
2802 },
2803 { } /* end */
2804};
2805
2806static struct hda_verb vt1708S_volume_init_verbs[] = {
2807 /* Unmute ADC0-1 and set the default input to mic-in */
2808 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2809 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2810
2811 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
2812 * analog-loopback mixer widget */
2813 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2814 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2815 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2816 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2817 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2818 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2819
2820 /* Setup default input of PW4 to MW0 */
2821 {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
Harald Welte5691ec72008-09-15 22:42:26 +08002822 /* PW9, PW10 Output enable */
Harald Welted949cac2008-09-09 15:56:01 +08002823 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Harald Welte5691ec72008-09-15 22:42:26 +08002824 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Harald Welted7426322008-09-15 22:43:23 +08002825 /* Enable Mic Boost Volume backdoor */
2826 {0x1, 0xf98, 0x1},
Harald Welted949cac2008-09-09 15:56:01 +08002827 { }
2828};
2829
Harald Welte69e52a82008-09-09 15:57:32 +08002830static struct hda_verb vt1708S_uniwill_init_verbs[] = {
2831 {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
2832 { }
2833};
2834
Harald Welted949cac2008-09-09 15:56:01 +08002835static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
2836 .substreams = 2,
2837 .channels_min = 2,
2838 .channels_max = 8,
2839 .nid = 0x10, /* NID to query formats and rates */
2840 .ops = {
2841 .open = via_playback_pcm_open,
2842 .prepare = via_playback_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08002843 .cleanup = via_playback_pcm_cleanup,
2844 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08002845 },
2846};
2847
2848static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
2849 .substreams = 2,
2850 .channels_min = 2,
2851 .channels_max = 2,
2852 .nid = 0x13, /* NID to query formats and rates */
2853 .ops = {
Lydia Wang17314372009-10-10 19:07:37 +08002854 .open = via_pcm_open_close,
Harald Welted949cac2008-09-09 15:56:01 +08002855 .prepare = via_capture_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08002856 .cleanup = via_capture_pcm_cleanup,
2857 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08002858 },
2859};
2860
2861static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
Takashi Iwai9da29272009-05-07 16:31:14 +02002862 .substreams = 1,
Harald Welted949cac2008-09-09 15:56:01 +08002863 .channels_min = 2,
2864 .channels_max = 2,
2865 /* NID is set in via_build_pcms */
2866 .ops = {
2867 .open = via_dig_playback_pcm_open,
2868 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02002869 .prepare = via_dig_playback_pcm_prepare,
2870 .cleanup = via_dig_playback_pcm_cleanup
Harald Welted949cac2008-09-09 15:56:01 +08002871 },
2872};
2873
2874/* fill in the dac_nids table from the parsed pin configuration */
2875static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
2876 const struct auto_pin_cfg *cfg)
2877{
2878 int i;
2879 hda_nid_t nid;
2880
2881 spec->multiout.num_dacs = cfg->line_outs;
2882
2883 spec->multiout.dac_nids = spec->private_dac_nids;
2884
2885 for (i = 0; i < 4; i++) {
2886 nid = cfg->line_out_pins[i];
2887 if (nid) {
2888 /* config dac list */
2889 switch (i) {
2890 case AUTO_SEQ_FRONT:
2891 spec->multiout.dac_nids[i] = 0x10;
2892 break;
2893 case AUTO_SEQ_CENLFE:
2894 spec->multiout.dac_nids[i] = 0x24;
2895 break;
2896 case AUTO_SEQ_SURROUND:
2897 spec->multiout.dac_nids[i] = 0x11;
2898 break;
2899 case AUTO_SEQ_SIDE:
2900 spec->multiout.dac_nids[i] = 0x25;
2901 break;
2902 }
2903 }
2904 }
2905
2906 return 0;
2907}
2908
2909/* add playback controls from the parsed DAC table */
2910static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
2911 const struct auto_pin_cfg *cfg)
2912{
2913 char name[32];
2914 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
2915 hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25};
2916 hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27};
2917 hda_nid_t nid, nid_vol, nid_mute;
2918 int i, err;
2919
2920 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2921 nid = cfg->line_out_pins[i];
2922
2923 if (!nid)
2924 continue;
2925
2926 nid_vol = nid_vols[i];
2927 nid_mute = nid_mutes[i];
2928
2929 if (i == AUTO_SEQ_CENLFE) {
2930 /* Center/LFE */
2931 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2932 "Center Playback Volume",
2933 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2934 HDA_OUTPUT));
2935 if (err < 0)
2936 return err;
2937 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2938 "LFE Playback Volume",
2939 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2940 HDA_OUTPUT));
2941 if (err < 0)
2942 return err;
2943 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2944 "Center Playback Switch",
2945 HDA_COMPOSE_AMP_VAL(nid_mute,
2946 1, 0,
2947 HDA_OUTPUT));
2948 if (err < 0)
2949 return err;
2950 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2951 "LFE Playback Switch",
2952 HDA_COMPOSE_AMP_VAL(nid_mute,
2953 2, 0,
2954 HDA_OUTPUT));
2955 if (err < 0)
2956 return err;
2957 } else if (i == AUTO_SEQ_FRONT) {
2958 /* add control to mixer index 0 */
2959 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2960 "Master Front Playback Volume",
2961 HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
2962 HDA_INPUT));
2963 if (err < 0)
2964 return err;
2965 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2966 "Master Front Playback Switch",
2967 HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
2968 HDA_INPUT));
2969 if (err < 0)
2970 return err;
2971
2972 /* Front */
2973 sprintf(name, "%s Playback Volume", chname[i]);
2974 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2975 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2976 HDA_OUTPUT));
2977 if (err < 0)
2978 return err;
2979 sprintf(name, "%s Playback Switch", chname[i]);
2980 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2981 HDA_COMPOSE_AMP_VAL(nid_mute,
2982 3, 0,
2983 HDA_OUTPUT));
2984 if (err < 0)
2985 return err;
2986 } else {
2987 sprintf(name, "%s Playback Volume", chname[i]);
2988 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2989 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2990 HDA_OUTPUT));
2991 if (err < 0)
2992 return err;
2993 sprintf(name, "%s Playback Switch", chname[i]);
2994 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2995 HDA_COMPOSE_AMP_VAL(nid_mute,
2996 3, 0,
2997 HDA_OUTPUT));
2998 if (err < 0)
2999 return err;
3000 }
3001 }
3002
3003 return 0;
3004}
3005
3006static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3007{
3008 int err;
3009
3010 if (!pin)
3011 return 0;
3012
3013 spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
3014
3015 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3016 "Headphone Playback Volume",
3017 HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
3018 if (err < 0)
3019 return err;
3020
3021 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3022 "Headphone Playback Switch",
3023 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3024 if (err < 0)
3025 return err;
3026
Harald Welte0aa62ae2008-09-09 15:58:27 +08003027 create_hp_imux(spec);
3028
Harald Welted949cac2008-09-09 15:56:01 +08003029 return 0;
3030}
3031
3032/* create playback/capture controls for input pins */
3033static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
3034 const struct auto_pin_cfg *cfg)
3035{
3036 static char *labels[] = {
3037 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
3038 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08003039 struct hda_input_mux *imux = &spec->private_imux[0];
Harald Welted949cac2008-09-09 15:56:01 +08003040 int i, err, idx = 0;
3041
3042 /* for internal loopback recording select */
3043 imux->items[imux->num_items].label = "Stereo Mixer";
3044 imux->items[imux->num_items].index = 5;
3045 imux->num_items++;
3046
3047 for (i = 0; i < AUTO_PIN_LAST; i++) {
3048 if (!cfg->input_pins[i])
3049 continue;
3050
3051 switch (cfg->input_pins[i]) {
3052 case 0x1a: /* Mic */
3053 idx = 2;
3054 break;
3055
3056 case 0x1b: /* Line In */
3057 idx = 3;
3058 break;
3059
3060 case 0x1e: /* Front Mic */
3061 idx = 4;
3062 break;
3063
3064 case 0x1f: /* CD */
3065 idx = 1;
3066 break;
3067 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08003068 err = via_new_analog_input(spec, labels[i], idx, 0x16);
Harald Welted949cac2008-09-09 15:56:01 +08003069 if (err < 0)
3070 return err;
3071 imux->items[imux->num_items].label = labels[i];
3072 imux->items[imux->num_items].index = idx-1;
3073 imux->num_items++;
3074 }
3075 return 0;
3076}
3077
Takashi Iwai9da29272009-05-07 16:31:14 +02003078/* fill out digital output widgets; one for master and one for slave outputs */
3079static void fill_dig_outs(struct hda_codec *codec)
3080{
3081 struct via_spec *spec = codec->spec;
3082 int i;
3083
3084 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3085 hda_nid_t nid;
3086 int conn;
3087
3088 nid = spec->autocfg.dig_out_pins[i];
3089 if (!nid)
3090 continue;
3091 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3092 if (conn < 1)
3093 continue;
3094 if (!spec->multiout.dig_out_nid)
3095 spec->multiout.dig_out_nid = nid;
3096 else {
3097 spec->slave_dig_outs[0] = nid;
3098 break; /* at most two dig outs */
3099 }
3100 }
3101}
3102
Harald Welted949cac2008-09-09 15:56:01 +08003103static int vt1708S_parse_auto_config(struct hda_codec *codec)
3104{
3105 struct via_spec *spec = codec->spec;
3106 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003107
Takashi Iwai9da29272009-05-07 16:31:14 +02003108 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
Harald Welted949cac2008-09-09 15:56:01 +08003109 if (err < 0)
3110 return err;
3111 err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
3112 if (err < 0)
3113 return err;
3114 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3115 return 0; /* can't find valid BIOS pin config */
3116
3117 err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg);
3118 if (err < 0)
3119 return err;
3120 err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3121 if (err < 0)
3122 return err;
3123 err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg);
3124 if (err < 0)
3125 return err;
3126
3127 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3128
Takashi Iwai9da29272009-05-07 16:31:14 +02003129 fill_dig_outs(codec);
Harald Welte98aa34c2008-09-09 16:02:09 +08003130
Takashi Iwai603c4012008-07-30 15:01:44 +02003131 if (spec->kctls.list)
3132 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Harald Welted949cac2008-09-09 15:56:01 +08003133
Harald Welte0aa62ae2008-09-09 15:58:27 +08003134 spec->input_mux = &spec->private_imux[0];
3135
Harald Weltef8fdd492008-09-15 22:41:31 +08003136 if (spec->hp_mux)
3137 spec->mixers[spec->num_mixers++] = via_hp_mixer;
Harald Welted949cac2008-09-09 15:56:01 +08003138
3139 return 1;
3140}
3141
3142#ifdef CONFIG_SND_HDA_POWER_SAVE
3143static struct hda_amp_list vt1708S_loopbacks[] = {
3144 { 0x16, HDA_INPUT, 1 },
3145 { 0x16, HDA_INPUT, 2 },
3146 { 0x16, HDA_INPUT, 3 },
3147 { 0x16, HDA_INPUT, 4 },
3148 { } /* end */
3149};
3150#endif
3151
3152static int patch_vt1708S(struct hda_codec *codec)
3153{
3154 struct via_spec *spec;
3155 int err;
3156
3157 /* create a codec specific record */
3158 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3159 if (spec == NULL)
3160 return -ENOMEM;
3161
3162 codec->spec = spec;
3163
3164 /* automatic parse from the BIOS config */
3165 err = vt1708S_parse_auto_config(codec);
3166 if (err < 0) {
3167 via_free(codec);
3168 return err;
3169 } else if (!err) {
3170 printk(KERN_INFO "hda_codec: Cannot set up configuration "
3171 "from BIOS. Using genenic mode...\n");
3172 }
3173
Harald Welte69e52a82008-09-09 15:57:32 +08003174 spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
3175 spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003176
3177 spec->stream_name_analog = "VT1708S Analog";
3178 spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
3179 spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
3180
3181 spec->stream_name_digital = "VT1708S Digital";
3182 spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
3183
3184 if (!spec->adc_nids && spec->input_mux) {
3185 spec->adc_nids = vt1708S_adc_nids;
3186 spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003187 get_mux_nids(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003188 spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
3189 spec->num_mixers++;
3190 }
3191
3192 codec->patch_ops = via_patch_ops;
3193
3194 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003195 codec->patch_ops.unsol_event = via_unsol_event;
Harald Welted949cac2008-09-09 15:56:01 +08003196#ifdef CONFIG_SND_HDA_POWER_SAVE
3197 spec->loopback.amplist = vt1708S_loopbacks;
3198#endif
3199
Lydia Wang518bf3b2009-10-10 19:07:29 +08003200 /* correct names for VT1708BCE */
3201 if (get_codec_type(codec) == VT1708BCE) {
3202 kfree(codec->chip_name);
3203 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3204 snprintf(codec->bus->card->mixername,
3205 sizeof(codec->bus->card->mixername),
3206 "%s %s", codec->vendor_name, codec->chip_name);
3207 spec->stream_name_analog = "VT1708BCE Analog";
3208 spec->stream_name_digital = "VT1708BCE Digital";
3209 }
Harald Welted949cac2008-09-09 15:56:01 +08003210 return 0;
3211}
3212
3213/* Patch for VT1702 */
3214
3215/* capture mixer elements */
3216static struct snd_kcontrol_new vt1702_capture_mixer[] = {
3217 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
3218 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
3219 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
3220 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
3221 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
3222 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
3223 HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
3224 HDA_INPUT),
3225 {
3226 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3227 /* The multiple "Capture Source" controls confuse alsamixer
3228 * So call somewhat different..
3229 */
3230 /* .name = "Capture Source", */
3231 .name = "Input Source",
3232 .count = 1,
3233 .info = via_mux_enum_info,
3234 .get = via_mux_enum_get,
3235 .put = via_mux_enum_put,
3236 },
3237 { } /* end */
3238};
3239
3240static struct hda_verb vt1702_volume_init_verbs[] = {
3241 /*
3242 * Unmute ADC0-1 and set the default input to mic-in
3243 */
3244 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3245 {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3246 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3247
3248
3249 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3250 * mixer widget
3251 */
3252 /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
3253 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3254 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3255 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3256 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3257 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3258
3259 /* Setup default input of PW4 to MW0 */
3260 {0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
3261 /* PW6 PW7 Output enable */
3262 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3263 {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3264 { }
3265};
3266
Harald Welte69e52a82008-09-09 15:57:32 +08003267static struct hda_verb vt1702_uniwill_init_verbs[] = {
3268 {0x01, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_GPIO_EVENT},
3269 {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
3270 { }
3271};
3272
Harald Welted949cac2008-09-09 15:56:01 +08003273static struct hda_pcm_stream vt1702_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08003274 .substreams = 2,
Harald Welted949cac2008-09-09 15:56:01 +08003275 .channels_min = 2,
3276 .channels_max = 2,
3277 .nid = 0x10, /* NID to query formats and rates */
3278 .ops = {
3279 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08003280 .prepare = via_playback_multi_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08003281 .cleanup = via_playback_multi_pcm_cleanup,
3282 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003283 },
3284};
3285
3286static struct hda_pcm_stream vt1702_pcm_analog_capture = {
3287 .substreams = 3,
3288 .channels_min = 2,
3289 .channels_max = 2,
3290 .nid = 0x12, /* NID to query formats and rates */
3291 .ops = {
Lydia Wang17314372009-10-10 19:07:37 +08003292 .open = via_pcm_open_close,
Harald Welted949cac2008-09-09 15:56:01 +08003293 .prepare = via_capture_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08003294 .cleanup = via_capture_pcm_cleanup,
3295 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003296 },
3297};
3298
3299static struct hda_pcm_stream vt1702_pcm_digital_playback = {
Harald Welte5691ec72008-09-15 22:42:26 +08003300 .substreams = 2,
Harald Welted949cac2008-09-09 15:56:01 +08003301 .channels_min = 2,
3302 .channels_max = 2,
3303 /* NID is set in via_build_pcms */
3304 .ops = {
3305 .open = via_dig_playback_pcm_open,
3306 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02003307 .prepare = via_dig_playback_pcm_prepare,
3308 .cleanup = via_dig_playback_pcm_cleanup
Harald Welted949cac2008-09-09 15:56:01 +08003309 },
3310};
3311
3312/* fill in the dac_nids table from the parsed pin configuration */
3313static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
3314 const struct auto_pin_cfg *cfg)
3315{
3316 spec->multiout.num_dacs = 1;
3317 spec->multiout.dac_nids = spec->private_dac_nids;
3318
3319 if (cfg->line_out_pins[0]) {
3320 /* config dac list */
3321 spec->multiout.dac_nids[0] = 0x10;
3322 }
3323
3324 return 0;
3325}
3326
3327/* add playback controls from the parsed DAC table */
3328static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
3329 const struct auto_pin_cfg *cfg)
3330{
3331 int err;
3332
3333 if (!cfg->line_out_pins[0])
3334 return -1;
3335
3336 /* add control to mixer index 0 */
3337 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3338 "Master Front Playback Volume",
3339 HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
3340 if (err < 0)
3341 return err;
3342 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3343 "Master Front Playback Switch",
3344 HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
3345 if (err < 0)
3346 return err;
3347
3348 /* Front */
3349 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3350 "Front Playback Volume",
3351 HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
3352 if (err < 0)
3353 return err;
3354 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3355 "Front Playback Switch",
3356 HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
3357 if (err < 0)
3358 return err;
3359
3360 return 0;
3361}
3362
3363static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3364{
Lydia Wang0713efe2009-10-10 19:07:43 +08003365 int err, i;
3366 struct hda_input_mux *imux;
3367 static const char *texts[] = { "ON", "OFF", NULL};
Harald Welted949cac2008-09-09 15:56:01 +08003368 if (!pin)
3369 return 0;
Harald Welted949cac2008-09-09 15:56:01 +08003370 spec->multiout.hp_nid = 0x1D;
3371
3372 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3373 "Headphone Playback Volume",
3374 HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
3375 if (err < 0)
3376 return err;
3377
3378 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3379 "Headphone Playback Switch",
3380 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3381 if (err < 0)
3382 return err;
3383
Lydia Wang0713efe2009-10-10 19:07:43 +08003384 imux = &spec->private_imux[1];
Harald Welte0aa62ae2008-09-09 15:58:27 +08003385
Lydia Wang0713efe2009-10-10 19:07:43 +08003386 /* for hp mode select */
3387 i = 0;
3388 while (texts[i] != NULL) {
3389 imux->items[imux->num_items].label = texts[i];
3390 imux->items[imux->num_items].index = i;
3391 imux->num_items++;
3392 i++;
3393 }
3394
3395 spec->hp_mux = &spec->private_imux[1];
Harald Welted949cac2008-09-09 15:56:01 +08003396 return 0;
3397}
3398
3399/* create playback/capture controls for input pins */
3400static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
3401 const struct auto_pin_cfg *cfg)
3402{
3403 static char *labels[] = {
3404 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
3405 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08003406 struct hda_input_mux *imux = &spec->private_imux[0];
Harald Welted949cac2008-09-09 15:56:01 +08003407 int i, err, idx = 0;
3408
3409 /* for internal loopback recording select */
3410 imux->items[imux->num_items].label = "Stereo Mixer";
3411 imux->items[imux->num_items].index = 3;
3412 imux->num_items++;
3413
3414 for (i = 0; i < AUTO_PIN_LAST; i++) {
3415 if (!cfg->input_pins[i])
3416 continue;
3417
3418 switch (cfg->input_pins[i]) {
3419 case 0x14: /* Mic */
3420 idx = 1;
3421 break;
3422
3423 case 0x15: /* Line In */
3424 idx = 2;
3425 break;
3426
3427 case 0x18: /* Front Mic */
3428 idx = 3;
3429 break;
3430 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08003431 err = via_new_analog_input(spec, labels[i], idx, 0x1A);
Harald Welted949cac2008-09-09 15:56:01 +08003432 if (err < 0)
3433 return err;
3434 imux->items[imux->num_items].label = labels[i];
3435 imux->items[imux->num_items].index = idx-1;
3436 imux->num_items++;
3437 }
3438 return 0;
3439}
3440
3441static int vt1702_parse_auto_config(struct hda_codec *codec)
3442{
3443 struct via_spec *spec = codec->spec;
3444 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003445
Takashi Iwai9da29272009-05-07 16:31:14 +02003446 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
Harald Welted949cac2008-09-09 15:56:01 +08003447 if (err < 0)
3448 return err;
3449 err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
3450 if (err < 0)
3451 return err;
3452 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3453 return 0; /* can't find valid BIOS pin config */
3454
3455 err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
3456 if (err < 0)
3457 return err;
3458 err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3459 if (err < 0)
3460 return err;
Lydia Wangc2c02ea2009-10-10 19:07:32 +08003461 /* limit AA path volume to 0 dB */
3462 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3463 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3464 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3465 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3466 (1 << AC_AMPCAP_MUTE_SHIFT));
Harald Welted949cac2008-09-09 15:56:01 +08003467 err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
3468 if (err < 0)
3469 return err;
3470
3471 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3472
Takashi Iwai9da29272009-05-07 16:31:14 +02003473 fill_dig_outs(codec);
Harald Welte98aa34c2008-09-09 16:02:09 +08003474
Takashi Iwai603c4012008-07-30 15:01:44 +02003475 if (spec->kctls.list)
3476 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Harald Welted949cac2008-09-09 15:56:01 +08003477
Harald Welte0aa62ae2008-09-09 15:58:27 +08003478 spec->input_mux = &spec->private_imux[0];
3479
Harald Weltef8fdd492008-09-15 22:41:31 +08003480 if (spec->hp_mux)
3481 spec->mixers[spec->num_mixers++] = via_hp_mixer;
Harald Welted949cac2008-09-09 15:56:01 +08003482
3483 return 1;
3484}
3485
3486#ifdef CONFIG_SND_HDA_POWER_SAVE
3487static struct hda_amp_list vt1702_loopbacks[] = {
3488 { 0x1A, HDA_INPUT, 1 },
3489 { 0x1A, HDA_INPUT, 2 },
3490 { 0x1A, HDA_INPUT, 3 },
3491 { 0x1A, HDA_INPUT, 4 },
3492 { } /* end */
3493};
3494#endif
3495
3496static int patch_vt1702(struct hda_codec *codec)
3497{
3498 struct via_spec *spec;
3499 int err;
3500 unsigned int response;
3501 unsigned char control;
3502
3503 /* create a codec specific record */
3504 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3505 if (spec == NULL)
3506 return -ENOMEM;
3507
3508 codec->spec = spec;
3509
3510 /* automatic parse from the BIOS config */
3511 err = vt1702_parse_auto_config(codec);
3512 if (err < 0) {
3513 via_free(codec);
3514 return err;
3515 } else if (!err) {
3516 printk(KERN_INFO "hda_codec: Cannot set up configuration "
3517 "from BIOS. Using genenic mode...\n");
3518 }
3519
Harald Welte69e52a82008-09-09 15:57:32 +08003520 spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
3521 spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003522
3523 spec->stream_name_analog = "VT1702 Analog";
3524 spec->stream_analog_playback = &vt1702_pcm_analog_playback;
3525 spec->stream_analog_capture = &vt1702_pcm_analog_capture;
3526
3527 spec->stream_name_digital = "VT1702 Digital";
3528 spec->stream_digital_playback = &vt1702_pcm_digital_playback;
3529
3530 if (!spec->adc_nids && spec->input_mux) {
3531 spec->adc_nids = vt1702_adc_nids;
3532 spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003533 get_mux_nids(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003534 spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
3535 spec->num_mixers++;
3536 }
3537
3538 codec->patch_ops = via_patch_ops;
3539
3540 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003541 codec->patch_ops.unsol_event = via_unsol_event;
Harald Welted949cac2008-09-09 15:56:01 +08003542#ifdef CONFIG_SND_HDA_POWER_SAVE
3543 spec->loopback.amplist = vt1702_loopbacks;
3544#endif
3545
3546 /* Open backdoor */
3547 response = snd_hda_codec_read(codec, codec->afg, 0, 0xF8C, 0);
3548 control = (unsigned char)(response & 0xff);
3549 control |= 0x3;
3550 snd_hda_codec_write(codec, codec->afg, 0, 0xF88, control);
3551
3552 /* Enable GPIO 0&1 for volume&mute control */
3553 /* Enable GPIO 2 for DMIC-DATA */
3554 response = snd_hda_codec_read(codec, codec->afg, 0, 0xF84, 0);
3555 control = (unsigned char)((response >> 16) & 0x3f);
3556 snd_hda_codec_write(codec, codec->afg, 0, 0xF82, control);
3557
3558 return 0;
3559}
3560
Joseph Chanc577b8a2006-11-29 15:29:40 +01003561/*
3562 * patch entries
3563 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003564static struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003565 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3566 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3567 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3568 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3569 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003570 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003571 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003572 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003573 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003574 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003575 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003576 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003577 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003578 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003579 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003580 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003581 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003582 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003583 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003584 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003585 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003586 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003587 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003588 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003589 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003590 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003591 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003592 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003593 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003594 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003595 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003596 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003597 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003598 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003599 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003600 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003601 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003602 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003603 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003604 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003605 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003606 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003607 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003608 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003609 { .id = 0x11064397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003610 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003611 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003612 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003613 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003614 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003615 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003616 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003617 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003618 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003619 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003620 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003621 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003622 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003623 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003624 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003625 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003626 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003627 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003628 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003629 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003630 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003631 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003632 .patch = patch_vt1702},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003633 {} /* terminator */
3634};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003635
3636MODULE_ALIAS("snd-hda-codec-id:1106*");
3637
3638static struct hda_codec_preset_list via_list = {
3639 .preset = snd_hda_preset_via,
3640 .owner = THIS_MODULE,
3641};
3642
3643MODULE_LICENSE("GPL");
3644MODULE_DESCRIPTION("VIA HD-audio codec");
3645
3646static int __init patch_via_init(void)
3647{
3648 return snd_hda_add_codec_preset(&via_list);
3649}
3650
3651static void __exit patch_via_exit(void)
3652{
3653 snd_hda_delete_codec_preset(&via_list);
3654}
3655
3656module_init(patch_via_init)
3657module_exit(patch_via_exit)