blob: 1c87231fa7e519bbd04093eaf7b3e7d41e92b4d2 [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 Wang1f2e99f2009-10-10 19:08:17 +080092struct via_spec {
93 /* codec parameterization */
94 struct snd_kcontrol_new *mixers[4];
95 unsigned int num_mixers;
96
97 struct hda_verb *init_verbs[5];
98 unsigned int num_iverbs;
99
100 char *stream_name_analog;
101 struct hda_pcm_stream *stream_analog_playback;
102 struct hda_pcm_stream *stream_analog_capture;
103
104 char *stream_name_digital;
105 struct hda_pcm_stream *stream_digital_playback;
106 struct hda_pcm_stream *stream_digital_capture;
107
108 /* playback */
109 struct hda_multi_out multiout;
110 hda_nid_t slave_dig_outs[2];
111
112 /* capture */
113 unsigned int num_adc_nids;
114 hda_nid_t *adc_nids;
115 hda_nid_t mux_nids[3];
116 hda_nid_t dig_in_nid;
117 hda_nid_t dig_in_pin;
118
119 /* capture source */
120 const struct hda_input_mux *input_mux;
121 unsigned int cur_mux[3];
122
123 /* PCM information */
124 struct hda_pcm pcm_rec[3];
125
126 /* dynamic controls, init_verbs and input_mux */
127 struct auto_pin_cfg autocfg;
128 struct snd_array kctls;
129 struct hda_input_mux private_imux[2];
130 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
131
132 /* HP mode source */
133 const struct hda_input_mux *hp_mux;
134 unsigned int hp_independent_mode;
135 unsigned int hp_independent_mode_index;
136 unsigned int smart51_enabled;
137
138 enum VIA_HDA_CODEC codec_type;
139
140 /* work to check hp jack state */
141 struct hda_codec *codec;
142 struct delayed_work vt1708_hp_work;
143 int vt1708_jack_detectect;
144 int vt1708_hp_present;
145#ifdef CONFIG_SND_HDA_POWER_SAVE
146 struct hda_loopback_check loopback;
147#endif
148};
149
Lydia Wang744ff5f2009-10-10 19:07:26 +0800150static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800151{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800152 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800153 u16 ven_id = vendor_id >> 16;
154 u16 dev_id = vendor_id & 0xffff;
155 enum VIA_HDA_CODEC codec_type;
156
157 /* get codec type */
158 if (ven_id != 0x1106)
159 codec_type = UNKNOWN;
160 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
161 codec_type = VT1708;
162 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
163 codec_type = VT1709_10CH;
164 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
165 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800166 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800167 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800168 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
169 codec_type = VT1708BCE;
170 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800171 codec_type = VT1708B_4CH;
172 else if ((dev_id & 0xfff) == 0x397
173 && (dev_id >> 12) < 8)
174 codec_type = VT1708S;
175 else if ((dev_id & 0xfff) == 0x398
176 && (dev_id >> 12) < 8)
177 codec_type = VT1702;
178 else
179 codec_type = UNKNOWN;
180 return codec_type;
181};
182
Harald Welte69e52a82008-09-09 15:57:32 +0800183#define VIA_HP_EVENT 0x01
184#define VIA_GPIO_EVENT 0x02
Lydia Wanga34df192009-10-10 19:08:01 +0800185#define VIA_JACK_EVENT 0x04
Harald Welte69e52a82008-09-09 15:57:32 +0800186
Joseph Chanc577b8a2006-11-29 15:29:40 +0100187enum {
188 VIA_CTL_WIDGET_VOL,
189 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800190 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100191};
192
193enum {
Harald Welteeb14a462008-09-09 15:40:38 +0800194 AUTO_SEQ_FRONT = 0,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100195 AUTO_SEQ_SURROUND,
196 AUTO_SEQ_CENLFE,
197 AUTO_SEQ_SIDE
198};
199
Lydia Wangf5271102009-10-10 19:07:35 +0800200static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
201static void set_jack_power_state(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800202static int is_aa_path_mute(struct hda_codec *codec);
203
204static void vt1708_start_hp_work(struct via_spec *spec)
205{
206 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
207 return;
208 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
209 !spec->vt1708_jack_detectect);
210 if (!delayed_work_pending(&spec->vt1708_hp_work))
211 schedule_delayed_work(&spec->vt1708_hp_work,
212 msecs_to_jiffies(100));
213}
214
215static void vt1708_stop_hp_work(struct via_spec *spec)
216{
217 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
218 return;
219 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
220 && !is_aa_path_mute(spec->codec))
221 return;
222 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
223 !spec->vt1708_jack_detectect);
224 cancel_delayed_work(&spec->vt1708_hp_work);
225 flush_scheduled_work();
226}
Lydia Wangf5271102009-10-10 19:07:35 +0800227
228static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
229 struct snd_ctl_elem_value *ucontrol)
230{
231 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
232 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
233
234 set_jack_power_state(codec);
235 analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800236 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
237 if (is_aa_path_mute(codec))
238 vt1708_start_hp_work(codec->spec);
239 else
240 vt1708_stop_hp_work(codec->spec);
241 }
Lydia Wangf5271102009-10-10 19:07:35 +0800242 return change;
243}
244
245/* modify .put = snd_hda_mixer_amp_switch_put */
246#define ANALOG_INPUT_MUTE \
247 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
248 .name = NULL, \
249 .index = 0, \
250 .info = snd_hda_mixer_amp_switch_info, \
251 .get = snd_hda_mixer_amp_switch_get, \
252 .put = analog_input_switch_put, \
253 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
254
Joseph Chanc577b8a2006-11-29 15:29:40 +0100255static struct snd_kcontrol_new vt1708_control_templates[] = {
256 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
257 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800258 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100259};
260
261
Joseph Chanc577b8a2006-11-29 15:29:40 +0100262static hda_nid_t vt1708_adc_nids[2] = {
263 /* ADC1-2 */
264 0x15, 0x27
265};
266
267static hda_nid_t vt1709_adc_nids[3] = {
268 /* ADC1-2 */
269 0x14, 0x15, 0x16
270};
271
Josepch Chanf7278fd2007-12-13 16:40:40 +0100272static hda_nid_t vt1708B_adc_nids[2] = {
273 /* ADC1-2 */
274 0x13, 0x14
275};
276
Harald Welted949cac2008-09-09 15:56:01 +0800277static hda_nid_t vt1708S_adc_nids[2] = {
278 /* ADC1-2 */
279 0x13, 0x14
280};
281
282static hda_nid_t vt1702_adc_nids[3] = {
283 /* ADC1-2 */
284 0x12, 0x20, 0x1F
285};
286
Joseph Chanc577b8a2006-11-29 15:29:40 +0100287/* add dynamic controls */
288static int via_add_control(struct via_spec *spec, int type, const char *name,
289 unsigned long val)
290{
291 struct snd_kcontrol_new *knew;
292
Takashi Iwai603c4012008-07-30 15:01:44 +0200293 snd_array_init(&spec->kctls, sizeof(*knew), 32);
294 knew = snd_array_new(&spec->kctls);
295 if (!knew)
296 return -ENOMEM;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100297 *knew = vt1708_control_templates[type];
298 knew->name = kstrdup(name, GFP_KERNEL);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100299 if (!knew->name)
300 return -ENOMEM;
301 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100302 return 0;
303}
304
Takashi Iwai603c4012008-07-30 15:01:44 +0200305static void via_free_kctls(struct hda_codec *codec)
306{
307 struct via_spec *spec = codec->spec;
308
309 if (spec->kctls.list) {
310 struct snd_kcontrol_new *kctl = spec->kctls.list;
311 int i;
312 for (i = 0; i < spec->kctls.used; i++)
313 kfree(kctl[i].name);
314 }
315 snd_array_free(&spec->kctls);
316}
317
Joseph Chanc577b8a2006-11-29 15:29:40 +0100318/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800319static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
320 int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100321{
322 char name[32];
323 int err;
324
325 sprintf(name, "%s Playback Volume", ctlname);
326 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
327 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
328 if (err < 0)
329 return err;
330 sprintf(name, "%s Playback Switch", ctlname);
Lydia Wangf5271102009-10-10 19:07:35 +0800331 err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100332 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
333 if (err < 0)
334 return err;
335 return 0;
336}
337
338static void via_auto_set_output_and_unmute(struct hda_codec *codec,
339 hda_nid_t nid, int pin_type,
340 int dac_idx)
341{
342 /* set as output */
343 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
344 pin_type);
345 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
346 AMP_OUT_UNMUTE);
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200347 if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
348 snd_hda_codec_write(codec, nid, 0,
349 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100350}
351
352
353static void via_auto_init_multi_out(struct hda_codec *codec)
354{
355 struct via_spec *spec = codec->spec;
356 int i;
357
358 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
359 hda_nid_t nid = spec->autocfg.line_out_pins[i];
360 if (nid)
361 via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
362 }
363}
364
365static void via_auto_init_hp_out(struct hda_codec *codec)
366{
367 struct via_spec *spec = codec->spec;
368 hda_nid_t pin;
369
370 pin = spec->autocfg.hp_pins[0];
371 if (pin) /* connect to front */
372 via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
373}
374
375static void via_auto_init_analog_input(struct hda_codec *codec)
376{
377 struct via_spec *spec = codec->spec;
378 int i;
379
380 for (i = 0; i < AUTO_PIN_LAST; i++) {
381 hda_nid_t nid = spec->autocfg.input_pins[i];
382
383 snd_hda_codec_write(codec, nid, 0,
384 AC_VERB_SET_PIN_WIDGET_CONTROL,
385 (i <= AUTO_PIN_FRONT_MIC ?
386 PIN_VREF50 : PIN_IN));
387
388 }
389}
Lydia Wangf5271102009-10-10 19:07:35 +0800390
Lydia Wang1564b282009-10-10 19:07:52 +0800391static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
392
Lydia Wangf5271102009-10-10 19:07:35 +0800393static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
394 unsigned int *affected_parm)
395{
396 unsigned parm;
397 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
398 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
399 >> AC_DEFCFG_MISC_SHIFT
400 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
401 unsigned present = snd_hda_codec_read(codec, nid, 0,
402 AC_VERB_GET_PIN_SENSE, 0) >> 31;
Lydia Wang1564b282009-10-10 19:07:52 +0800403 struct via_spec *spec = codec->spec;
404 if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
405 || ((no_presence || present)
406 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800407 *affected_parm = AC_PWRST_D0; /* if it's connected */
408 parm = AC_PWRST_D0;
409 } else
410 parm = AC_PWRST_D3;
411
412 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
413}
414
415static void set_jack_power_state(struct hda_codec *codec)
416{
417 struct via_spec *spec = codec->spec;
418 int imux_is_smixer;
419 unsigned int parm;
420
421 if (spec->codec_type == VT1702) {
422 imux_is_smixer = snd_hda_codec_read(
423 codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
424 /* inputs */
425 /* PW 1/2/5 (14h/15h/18h) */
426 parm = AC_PWRST_D3;
427 set_pin_power_state(codec, 0x14, &parm);
428 set_pin_power_state(codec, 0x15, &parm);
429 set_pin_power_state(codec, 0x18, &parm);
430 if (imux_is_smixer)
431 parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */
432 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
433 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
434 parm);
435 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE,
436 parm);
437 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
438 parm);
439 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE,
440 parm);
441
442 /* outputs */
443 /* PW 3/4 (16h/17h) */
444 parm = AC_PWRST_D3;
445 set_pin_power_state(codec, 0x16, &parm);
446 set_pin_power_state(codec, 0x17, &parm);
447 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
448 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
449 imux_is_smixer ? AC_PWRST_D0 : parm);
450 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
451 parm);
452 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE,
453 parm);
454 } else if (spec->codec_type == VT1708B_8CH
455 || spec->codec_type == VT1708B_4CH
456 || spec->codec_type == VT1708S) {
457 /* SW0 (17h) = stereo mixer */
458 int is_8ch = spec->codec_type != VT1708B_4CH;
459 imux_is_smixer = snd_hda_codec_read(
460 codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
461 == ((spec->codec_type == VT1708S) ? 5 : 0);
462 /* inputs */
463 /* PW 1/2/5 (1ah/1bh/1eh) */
464 parm = AC_PWRST_D3;
465 set_pin_power_state(codec, 0x1a, &parm);
466 set_pin_power_state(codec, 0x1b, &parm);
467 set_pin_power_state(codec, 0x1e, &parm);
468 if (imux_is_smixer)
469 parm = AC_PWRST_D0;
470 /* SW0 (17h), AIW 0/1 (13h/14h) */
471 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
472 parm);
473 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
474 parm);
475 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
476 parm);
477
478 /* outputs */
479 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
480 parm = AC_PWRST_D3;
481 set_pin_power_state(codec, 0x19, &parm);
482 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
483 parm);
484 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
485 parm);
486
487 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
488 if (is_8ch) {
489 parm = AC_PWRST_D3;
490 set_pin_power_state(codec, 0x22, &parm);
491 snd_hda_codec_write(codec, 0x26, 0,
492 AC_VERB_SET_POWER_STATE, parm);
493 snd_hda_codec_write(codec, 0x24, 0,
494 AC_VERB_SET_POWER_STATE, parm);
495 }
496
497 /* PW 3/4/7 (1ch/1dh/23h) */
498 parm = AC_PWRST_D3;
499 /* force to D0 for internal Speaker */
500 set_pin_power_state(codec, 0x1c, &parm);
501 set_pin_power_state(codec, 0x1d, &parm);
502 if (is_8ch)
503 set_pin_power_state(codec, 0x23, &parm);
504 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
505 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
506 imux_is_smixer ? AC_PWRST_D0 : parm);
507 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
508 parm);
509 if (is_8ch) {
510 snd_hda_codec_write(codec, 0x25, 0,
511 AC_VERB_SET_POWER_STATE, parm);
512 snd_hda_codec_write(codec, 0x27, 0,
513 AC_VERB_SET_POWER_STATE, parm);
514 }
515 }
516}
517
Joseph Chanc577b8a2006-11-29 15:29:40 +0100518/*
519 * input MUX handling
520 */
521static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
522 struct snd_ctl_elem_info *uinfo)
523{
524 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
525 struct via_spec *spec = codec->spec;
526 return snd_hda_input_mux_info(spec->input_mux, uinfo);
527}
528
529static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
530 struct snd_ctl_elem_value *ucontrol)
531{
532 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
533 struct via_spec *spec = codec->spec;
534 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
535
536 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
537 return 0;
538}
539
540static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
541 struct snd_ctl_elem_value *ucontrol)
542{
543 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
544 struct via_spec *spec = codec->spec;
545 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100546
Takashi Iwai337b9d02009-07-07 18:18:59 +0200547 if (!spec->mux_nids[adc_idx])
548 return -EINVAL;
Lydia Wanga80e6e32009-10-10 19:07:55 +0800549 /* switch to D0 beofre change index */
550 if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
551 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
552 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
553 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
554 /* update jack power state */
555 set_jack_power_state(codec);
556
Takashi Iwai337b9d02009-07-07 18:18:59 +0200557 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
558 spec->mux_nids[adc_idx],
559 &spec->cur_mux[adc_idx]);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100560}
561
Harald Welte0aa62ae2008-09-09 15:58:27 +0800562static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
563 struct snd_ctl_elem_info *uinfo)
564{
565 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
566 struct via_spec *spec = codec->spec;
567 return snd_hda_input_mux_info(spec->hp_mux, uinfo);
568}
569
570static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
571 struct snd_ctl_elem_value *ucontrol)
572{
573 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
574 struct via_spec *spec = codec->spec;
575 hda_nid_t nid = spec->autocfg.hp_pins[0];
576 unsigned int pinsel = snd_hda_codec_read(codec, nid, 0,
577 AC_VERB_GET_CONNECT_SEL,
578 0x00);
579
580 ucontrol->value.enumerated.item[0] = pinsel;
581
582 return 0;
583}
584
Lydia Wang0713efe2009-10-10 19:07:43 +0800585static void activate_ctl(struct hda_codec *codec, const char *name, int active)
586{
587 struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
588 if (ctl) {
589 ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
590 ctl->vd[0].access |= active
591 ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
592 snd_ctl_notify(codec->bus->card,
593 SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
594 }
595}
596
Lydia Wangcdc17842009-10-10 19:07:47 +0800597static int update_side_mute_status(struct hda_codec *codec)
598{
599 /* mute side channel */
600 struct via_spec *spec = codec->spec;
601 unsigned int parm = spec->hp_independent_mode
602 ? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
603 hda_nid_t sw3;
604
605 switch (spec->codec_type) {
606 case VT1708:
607 sw3 = 0x1b;
608 break;
609 case VT1709_10CH:
610 sw3 = 0x29;
611 break;
612 case VT1708B_8CH:
613 case VT1708S:
614 sw3 = 0x27;
615 break;
616 default:
617 sw3 = 0;
618 break;
619 }
620
621 if (sw3)
622 snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
623 parm);
624 return 0;
625}
626
Harald Welte0aa62ae2008-09-09 15:58:27 +0800627static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
628 struct snd_ctl_elem_value *ucontrol)
629{
630 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
631 struct via_spec *spec = codec->spec;
632 hda_nid_t nid = spec->autocfg.hp_pins[0];
633 unsigned int pinsel = ucontrol->value.enumerated.item[0];
Lydia Wangcdc17842009-10-10 19:07:47 +0800634 /* Get Independent Mode index of headphone pin widget */
635 spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
636 ? 1 : 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800637
Lydia Wangcdc17842009-10-10 19:07:47 +0800638 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800639
Lydia Wangcdc17842009-10-10 19:07:47 +0800640 if (spec->multiout.hp_nid && spec->multiout.hp_nid
641 != spec->multiout.dac_nids[HDA_FRONT])
642 snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
643 0, 0, 0);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800644
Lydia Wangcdc17842009-10-10 19:07:47 +0800645 update_side_mute_status(codec);
Lydia Wang0713efe2009-10-10 19:07:43 +0800646 /* update HP volume/swtich active state */
647 if (spec->codec_type == VT1708S
648 || spec->codec_type == VT1702) {
649 activate_ctl(codec, "Headphone Playback Volume",
650 spec->hp_independent_mode);
651 activate_ctl(codec, "Headphone Playback Switch",
652 spec->hp_independent_mode);
653 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800654 return 0;
655}
656
657static struct snd_kcontrol_new via_hp_mixer[] = {
658 {
659 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
660 .name = "Independent HP",
661 .count = 1,
662 .info = via_independent_hp_info,
663 .get = via_independent_hp_get,
664 .put = via_independent_hp_put,
665 },
666 { } /* end */
667};
668
Lydia Wang1564b282009-10-10 19:07:52 +0800669static void notify_aa_path_ctls(struct hda_codec *codec)
670{
671 int i;
672 struct snd_ctl_elem_id id;
673 const char *labels[] = {"Mic", "Front Mic", "Line"};
674
675 memset(&id, 0, sizeof(id));
676 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
677 for (i = 0; i < ARRAY_SIZE(labels); i++) {
678 sprintf(id.name, "%s Playback Volume", labels[i]);
679 snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
680 &id);
681 }
682}
683
684static void mute_aa_path(struct hda_codec *codec, int mute)
685{
686 struct via_spec *spec = codec->spec;
687 hda_nid_t nid_mixer;
688 int start_idx;
689 int end_idx;
690 int i;
691 /* get nid of MW0 and start & end index */
692 switch (spec->codec_type) {
693 case VT1708:
694 nid_mixer = 0x17;
695 start_idx = 2;
696 end_idx = 4;
697 break;
698 case VT1709_10CH:
699 case VT1709_6CH:
700 nid_mixer = 0x18;
701 start_idx = 2;
702 end_idx = 4;
703 break;
704 case VT1708B_8CH:
705 case VT1708B_4CH:
706 case VT1708S:
707 nid_mixer = 0x16;
708 start_idx = 2;
709 end_idx = 4;
710 break;
711 default:
712 return;
713 }
714 /* check AA path's mute status */
715 for (i = start_idx; i <= end_idx; i++) {
716 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
717 snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
718 HDA_AMP_MUTE, val);
719 }
720}
721static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
722{
723 int res = 0;
724 int index;
725 for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
726 if (pin == spec->autocfg.input_pins[index]) {
727 res = 1;
728 break;
729 }
730 }
731 return res;
732}
733
734static int via_smart51_info(struct snd_kcontrol *kcontrol,
735 struct snd_ctl_elem_info *uinfo)
736{
737 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
738 uinfo->count = 1;
739 uinfo->value.integer.min = 0;
740 uinfo->value.integer.max = 1;
741 return 0;
742}
743
744static int via_smart51_get(struct snd_kcontrol *kcontrol,
745 struct snd_ctl_elem_value *ucontrol)
746{
747 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
748 struct via_spec *spec = codec->spec;
749 int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
750 int on = 1;
751 int i;
752
753 for (i = 0; i < ARRAY_SIZE(index); i++) {
754 hda_nid_t nid = spec->autocfg.input_pins[index[i]];
755 if (nid) {
756 int ctl =
757 snd_hda_codec_read(codec, nid, 0,
758 AC_VERB_GET_PIN_WIDGET_CONTROL,
759 0);
760 if (i == AUTO_PIN_FRONT_MIC
761 && spec->hp_independent_mode)
762 continue; /* ignore FMic for independent HP */
763 if (ctl & AC_PINCTL_IN_EN
764 && !(ctl & AC_PINCTL_OUT_EN))
765 on = 0;
766 }
767 }
768 *ucontrol->value.integer.value = on;
769 return 0;
770}
771
772static int via_smart51_put(struct snd_kcontrol *kcontrol,
773 struct snd_ctl_elem_value *ucontrol)
774{
775 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
776 struct via_spec *spec = codec->spec;
777 int out_in = *ucontrol->value.integer.value
778 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
779 int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
780 int i;
781
782 for (i = 0; i < ARRAY_SIZE(index); i++) {
783 hda_nid_t nid = spec->autocfg.input_pins[index[i]];
784 if (i == AUTO_PIN_FRONT_MIC
785 && spec->hp_independent_mode)
786 continue; /* don't retask FMic for independent HP */
787 if (nid) {
788 unsigned int parm = snd_hda_codec_read(
789 codec, nid, 0,
790 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
791 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
792 parm |= out_in;
793 snd_hda_codec_write(codec, nid, 0,
794 AC_VERB_SET_PIN_WIDGET_CONTROL,
795 parm);
796 if (out_in == AC_PINCTL_OUT_EN) {
797 mute_aa_path(codec, 1);
798 notify_aa_path_ctls(codec);
799 }
800 }
801 if (i == AUTO_PIN_FRONT_MIC) {
802 if (spec->codec_type == VT1708S) {
803 /* input = index 1 (AOW3) */
804 snd_hda_codec_write(
805 codec, nid, 0,
806 AC_VERB_SET_CONNECT_SEL, 1);
807 snd_hda_codec_amp_stereo(
808 codec, nid, HDA_OUTPUT,
809 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
810 }
811 }
812 }
813 spec->smart51_enabled = *ucontrol->value.integer.value;
814 set_jack_power_state(codec);
815 return 1;
816}
817
818static struct snd_kcontrol_new via_smart51_mixer[] = {
819 {
820 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
821 .name = "Smart 5.1",
822 .count = 1,
823 .info = via_smart51_info,
824 .get = via_smart51_get,
825 .put = via_smart51_put,
826 },
827 {} /* end */
828};
829
Joseph Chanc577b8a2006-11-29 15:29:40 +0100830/* capture mixer elements */
831static struct snd_kcontrol_new vt1708_capture_mixer[] = {
832 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
833 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
834 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
835 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
836 {
837 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
838 /* The multiple "Capture Source" controls confuse alsamixer
839 * So call somewhat different..
Joseph Chanc577b8a2006-11-29 15:29:40 +0100840 */
841 /* .name = "Capture Source", */
842 .name = "Input Source",
843 .count = 1,
844 .info = via_mux_enum_info,
845 .get = via_mux_enum_get,
846 .put = via_mux_enum_put,
847 },
848 { } /* end */
849};
Lydia Wangf5271102009-10-10 19:07:35 +0800850
851/* check AA path's mute statue */
852static int is_aa_path_mute(struct hda_codec *codec)
853{
854 int mute = 1;
855 hda_nid_t nid_mixer;
856 int start_idx;
857 int end_idx;
858 int i;
859 struct via_spec *spec = codec->spec;
860 /* get nid of MW0 and start & end index */
861 switch (spec->codec_type) {
862 case VT1708B_8CH:
863 case VT1708B_4CH:
864 case VT1708S:
865 nid_mixer = 0x16;
866 start_idx = 2;
867 end_idx = 4;
868 break;
869 case VT1702:
870 nid_mixer = 0x1a;
871 start_idx = 1;
872 end_idx = 3;
873 break;
874 default:
875 return 0;
876 }
877 /* check AA path's mute status */
878 for (i = start_idx; i <= end_idx; i++) {
879 unsigned int con_list = snd_hda_codec_read(
880 codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
881 int shift = 8 * (i % 4);
882 hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
883 unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
884 if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
885 /* check mute status while the pin is connected */
886 int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
887 HDA_INPUT, i) >> 7;
888 int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
889 HDA_INPUT, i) >> 7;
890 if (!mute_l || !mute_r) {
891 mute = 0;
892 break;
893 }
894 }
895 }
896 return mute;
897}
898
899/* enter/exit analog low-current mode */
900static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
901{
902 struct via_spec *spec = codec->spec;
903 static int saved_stream_idle = 1; /* saved stream idle status */
904 int enable = is_aa_path_mute(codec);
905 unsigned int verb = 0;
906 unsigned int parm = 0;
907
908 if (stream_idle == -1) /* stream status did not change */
909 enable = enable && saved_stream_idle;
910 else {
911 enable = enable && stream_idle;
912 saved_stream_idle = stream_idle;
913 }
914
915 /* decide low current mode's verb & parameter */
916 switch (spec->codec_type) {
917 case VT1708B_8CH:
918 case VT1708B_4CH:
919 verb = 0xf70;
920 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
921 break;
922 case VT1708S:
923 verb = 0xf73;
924 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
925 break;
926 case VT1702:
927 verb = 0xf73;
928 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
929 break;
930 default:
931 return; /* other codecs are not supported */
932 }
933 /* send verb */
934 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
935}
936
Joseph Chanc577b8a2006-11-29 15:29:40 +0100937/*
938 * generic initialization of ADC, input mixers and output mixers
939 */
940static struct hda_verb vt1708_volume_init_verbs[] = {
941 /*
942 * Unmute ADC0-1 and set the default input to mic-in
943 */
944 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
945 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
946
947
Josepch Chanf7278fd2007-12-13 16:40:40 +0100948 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
Joseph Chanc577b8a2006-11-29 15:29:40 +0100949 * mixer widget
950 */
951 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
Josepch Chanf7278fd2007-12-13 16:40:40 +0100952 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
953 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
954 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
955 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
956 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
Joseph Chanc577b8a2006-11-29 15:29:40 +0100957
958 /*
959 * Set up output mixers (0x19 - 0x1b)
960 */
961 /* set vol=0 to output mixers */
962 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
963 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
964 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
965
966 /* Setup default input to PW4 */
967 {0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
Joseph Chanc577b8a2006-11-29 15:29:40 +0100968 /* PW9 Output enable */
969 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100970 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100971};
972
973static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
974 struct hda_codec *codec,
975 struct snd_pcm_substream *substream)
976{
977 struct via_spec *spec = codec->spec;
Lydia Wang17314372009-10-10 19:07:37 +0800978 int idle = substream->pstr->substream_opened == 1
979 && substream->ref_count == 0;
Lydia Wang17314372009-10-10 19:07:37 +0800980 analog_low_current_mode(codec, idle);
Takashi Iwai9a081602008-02-12 18:37:26 +0100981 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
982 hinfo);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100983}
984
Harald Welte0aa62ae2008-09-09 15:58:27 +0800985static void playback_multi_pcm_prep_0(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;
991 struct hda_multi_out *mout = &spec->multiout;
992 hda_nid_t *nids = mout->dac_nids;
993 int chs = substream->runtime->channels;
994 int i;
995
996 mutex_lock(&codec->spdif_mutex);
997 if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
998 if (chs == 2 &&
999 snd_hda_is_supported_format(codec, mout->dig_out_nid,
1000 format) &&
1001 !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
1002 mout->dig_out_used = HDA_DIG_ANALOG_DUP;
1003 /* turn off SPDIF once; otherwise the IEC958 bits won't
1004 * be updated */
1005 if (codec->spdif_ctls & AC_DIG1_ENABLE)
1006 snd_hda_codec_write(codec, mout->dig_out_nid, 0,
1007 AC_VERB_SET_DIGI_CONVERT_1,
1008 codec->spdif_ctls &
1009 ~AC_DIG1_ENABLE & 0xff);
1010 snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
1011 stream_tag, 0, format);
1012 /* turn on again (if needed) */
1013 if (codec->spdif_ctls & AC_DIG1_ENABLE)
1014 snd_hda_codec_write(codec, mout->dig_out_nid, 0,
1015 AC_VERB_SET_DIGI_CONVERT_1,
1016 codec->spdif_ctls & 0xff);
1017 } else {
1018 mout->dig_out_used = 0;
1019 snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
1020 0, 0, 0);
1021 }
1022 }
1023 mutex_unlock(&codec->spdif_mutex);
1024
1025 /* front */
1026 snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
1027 0, format);
1028
1029 if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
1030 !spec->hp_independent_mode)
1031 /* headphone out will just decode front left/right (stereo) */
1032 snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
1033 0, format);
1034
1035 /* extra outputs copied from front */
1036 for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
1037 if (mout->extra_out_nid[i])
1038 snd_hda_codec_setup_stream(codec,
1039 mout->extra_out_nid[i],
1040 stream_tag, 0, format);
1041
1042 /* surrounds */
1043 for (i = 1; i < mout->num_dacs; i++) {
1044 if (chs >= (i + 1) * 2) /* independent out */
1045 snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
1046 i * 2, format);
1047 else /* copy front */
1048 snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
1049 0, format);
1050 }
1051}
1052
1053static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1054 struct hda_codec *codec,
1055 unsigned int stream_tag,
1056 unsigned int format,
1057 struct snd_pcm_substream *substream)
1058{
1059 struct via_spec *spec = codec->spec;
1060 struct hda_multi_out *mout = &spec->multiout;
1061 hda_nid_t *nids = mout->dac_nids;
1062
1063 if (substream->number == 0)
1064 playback_multi_pcm_prep_0(codec, stream_tag, format,
1065 substream);
1066 else {
1067 if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
1068 spec->hp_independent_mode)
1069 snd_hda_codec_setup_stream(codec, mout->hp_nid,
1070 stream_tag, 0, format);
1071 }
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001072 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001073 return 0;
1074}
1075
1076static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1077 struct hda_codec *codec,
1078 struct snd_pcm_substream *substream)
1079{
1080 struct via_spec *spec = codec->spec;
1081 struct hda_multi_out *mout = &spec->multiout;
1082 hda_nid_t *nids = mout->dac_nids;
1083 int i;
1084
1085 if (substream->number == 0) {
1086 for (i = 0; i < mout->num_dacs; i++)
1087 snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
1088
1089 if (mout->hp_nid && !spec->hp_independent_mode)
1090 snd_hda_codec_setup_stream(codec, mout->hp_nid,
1091 0, 0, 0);
1092
1093 for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
1094 if (mout->extra_out_nid[i])
1095 snd_hda_codec_setup_stream(codec,
1096 mout->extra_out_nid[i],
1097 0, 0, 0);
1098 mutex_lock(&codec->spdif_mutex);
1099 if (mout->dig_out_nid &&
1100 mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
1101 snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
1102 0, 0, 0);
1103 mout->dig_out_used = 0;
1104 }
1105 mutex_unlock(&codec->spdif_mutex);
1106 } else {
1107 if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
1108 spec->hp_independent_mode)
1109 snd_hda_codec_setup_stream(codec, mout->hp_nid,
1110 0, 0, 0);
1111 }
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001112 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001113 return 0;
1114}
1115
Joseph Chanc577b8a2006-11-29 15:29:40 +01001116/*
1117 * Digital out
1118 */
1119static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1120 struct hda_codec *codec,
1121 struct snd_pcm_substream *substream)
1122{
1123 struct via_spec *spec = codec->spec;
1124 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1125}
1126
1127static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1128 struct hda_codec *codec,
1129 struct snd_pcm_substream *substream)
1130{
1131 struct via_spec *spec = codec->spec;
1132 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1133}
1134
Harald Welte5691ec72008-09-15 22:42:26 +08001135static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001136 struct hda_codec *codec,
1137 unsigned int stream_tag,
1138 unsigned int format,
1139 struct snd_pcm_substream *substream)
1140{
1141 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001142 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1143 stream_tag, format, substream);
1144}
Harald Welte5691ec72008-09-15 22:42:26 +08001145
Takashi Iwai9da29272009-05-07 16:31:14 +02001146static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1147 struct hda_codec *codec,
1148 struct snd_pcm_substream *substream)
1149{
1150 struct via_spec *spec = codec->spec;
1151 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001152 return 0;
1153}
1154
Joseph Chanc577b8a2006-11-29 15:29:40 +01001155/*
1156 * Analog capture
1157 */
1158static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1159 struct hda_codec *codec,
1160 unsigned int stream_tag,
1161 unsigned int format,
1162 struct snd_pcm_substream *substream)
1163{
1164 struct via_spec *spec = codec->spec;
1165
1166 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1167 stream_tag, 0, format);
1168 return 0;
1169}
1170
1171static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1172 struct hda_codec *codec,
1173 struct snd_pcm_substream *substream)
1174{
1175 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001176 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001177 return 0;
1178}
1179
1180static struct hda_pcm_stream vt1708_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08001181 .substreams = 2,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001182 .channels_min = 2,
1183 .channels_max = 8,
1184 .nid = 0x10, /* NID to query formats and rates */
1185 .ops = {
1186 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001187 .prepare = via_playback_multi_pcm_prepare,
1188 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001189 },
1190};
1191
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001192static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Lydia Wangc873cc22009-10-10 19:08:21 +08001193 .substreams = 2,
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001194 .channels_min = 2,
1195 .channels_max = 8,
1196 .nid = 0x10, /* NID to query formats and rates */
1197 /* We got noisy outputs on the right channel on VT1708 when
1198 * 24bit samples are used. Until any workaround is found,
1199 * disable the 24bit format, so far.
1200 */
1201 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1202 .ops = {
1203 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08001204 .prepare = via_playback_multi_pcm_prepare,
1205 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001206 },
1207};
1208
Joseph Chanc577b8a2006-11-29 15:29:40 +01001209static struct hda_pcm_stream vt1708_pcm_analog_capture = {
1210 .substreams = 2,
1211 .channels_min = 2,
1212 .channels_max = 2,
1213 .nid = 0x15, /* NID to query formats and rates */
1214 .ops = {
1215 .prepare = via_capture_pcm_prepare,
1216 .cleanup = via_capture_pcm_cleanup
1217 },
1218};
1219
1220static struct hda_pcm_stream vt1708_pcm_digital_playback = {
1221 .substreams = 1,
1222 .channels_min = 2,
1223 .channels_max = 2,
1224 /* NID is set in via_build_pcms */
1225 .ops = {
1226 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001227 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001228 .prepare = via_dig_playback_pcm_prepare,
1229 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001230 },
1231};
1232
1233static struct hda_pcm_stream vt1708_pcm_digital_capture = {
1234 .substreams = 1,
1235 .channels_min = 2,
1236 .channels_max = 2,
1237};
1238
1239static int via_build_controls(struct hda_codec *codec)
1240{
1241 struct via_spec *spec = codec->spec;
1242 int err;
1243 int i;
1244
1245 for (i = 0; i < spec->num_mixers; i++) {
1246 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1247 if (err < 0)
1248 return err;
1249 }
1250
1251 if (spec->multiout.dig_out_nid) {
1252 err = snd_hda_create_spdif_out_ctls(codec,
1253 spec->multiout.dig_out_nid);
1254 if (err < 0)
1255 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001256 err = snd_hda_create_spdif_share_sw(codec,
1257 &spec->multiout);
1258 if (err < 0)
1259 return err;
1260 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001261 }
1262 if (spec->dig_in_nid) {
1263 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1264 if (err < 0)
1265 return err;
1266 }
Lydia Wang17314372009-10-10 19:07:37 +08001267
1268 /* init power states */
1269 set_jack_power_state(codec);
1270 analog_low_current_mode(codec, 1);
1271
Takashi Iwai603c4012008-07-30 15:01:44 +02001272 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001273 return 0;
1274}
1275
1276static int via_build_pcms(struct hda_codec *codec)
1277{
1278 struct via_spec *spec = codec->spec;
1279 struct hda_pcm *info = spec->pcm_rec;
1280
1281 codec->num_pcms = 1;
1282 codec->pcm_info = info;
1283
1284 info->name = spec->stream_name_analog;
1285 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
1286 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
1287 info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1288 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1289
1290 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1291 spec->multiout.max_channels;
1292
1293 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1294 codec->num_pcms++;
1295 info++;
1296 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001297 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001298 if (spec->multiout.dig_out_nid) {
1299 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1300 *(spec->stream_digital_playback);
1301 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1302 spec->multiout.dig_out_nid;
1303 }
1304 if (spec->dig_in_nid) {
1305 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1306 *(spec->stream_digital_capture);
1307 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1308 spec->dig_in_nid;
1309 }
1310 }
1311
1312 return 0;
1313}
1314
1315static void via_free(struct hda_codec *codec)
1316{
1317 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001318
1319 if (!spec)
1320 return;
1321
Takashi Iwai603c4012008-07-30 15:01:44 +02001322 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001323 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001324 kfree(codec->spec);
1325}
1326
Harald Welte69e52a82008-09-09 15:57:32 +08001327/* mute internal speaker if HP is plugged */
1328static void via_hp_automute(struct hda_codec *codec)
1329{
Lydia Wangdcf34c82009-10-10 19:08:15 +08001330 unsigned int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001331 struct via_spec *spec = codec->spec;
1332
1333 present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
1334 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
Lydia Wangdcf34c82009-10-10 19:08:15 +08001335
1336 if (!spec->hp_independent_mode) {
1337 struct snd_ctl_elem_id id;
1338 /* auto mute */
1339 snd_hda_codec_amp_stereo(
1340 codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
1341 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
1342 /* notify change */
1343 memset(&id, 0, sizeof(id));
1344 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
1345 strcpy(id.name, "Front Playback Switch");
1346 snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
1347 &id);
1348 }
Harald Welte69e52a82008-09-09 15:57:32 +08001349}
1350
1351static void via_gpio_control(struct hda_codec *codec)
1352{
1353 unsigned int gpio_data;
1354 unsigned int vol_counter;
1355 unsigned int vol;
1356 unsigned int master_vol;
1357
1358 struct via_spec *spec = codec->spec;
1359
1360 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1361 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1362
1363 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1364 0xF84, 0) & 0x3F0000) >> 16;
1365
1366 vol = vol_counter & 0x1F;
1367 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1368 AC_VERB_GET_AMP_GAIN_MUTE,
1369 AC_AMP_GET_INPUT);
1370
1371 if (gpio_data == 0x02) {
1372 /* unmute line out */
1373 snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
1374 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
1375
1376 if (vol_counter & 0x20) {
1377 /* decrease volume */
1378 if (vol > master_vol)
1379 vol = master_vol;
1380 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1381 0, HDA_AMP_VOLMASK,
1382 master_vol-vol);
1383 } else {
1384 /* increase volume */
1385 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1386 HDA_AMP_VOLMASK,
1387 ((master_vol+vol) > 0x2A) ? 0x2A :
1388 (master_vol+vol));
1389 }
1390 } else if (!(gpio_data & 0x02)) {
1391 /* mute line out */
1392 snd_hda_codec_amp_stereo(codec,
1393 spec->autocfg.line_out_pins[0],
1394 HDA_OUTPUT, 0, HDA_AMP_MUTE,
1395 HDA_AMP_MUTE);
1396 }
1397}
1398
1399/* unsolicited event for jack sensing */
1400static void via_unsol_event(struct hda_codec *codec,
1401 unsigned int res)
1402{
1403 res >>= 26;
Lydia Wanga34df192009-10-10 19:08:01 +08001404 if (res & VIA_HP_EVENT)
Harald Welte69e52a82008-09-09 15:57:32 +08001405 via_hp_automute(codec);
Lydia Wanga34df192009-10-10 19:08:01 +08001406 if (res & VIA_GPIO_EVENT)
Harald Welte69e52a82008-09-09 15:57:32 +08001407 via_gpio_control(codec);
Lydia Wanga34df192009-10-10 19:08:01 +08001408 if (res & VIA_JACK_EVENT)
1409 set_jack_power_state(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001410}
1411
Joseph Chanc577b8a2006-11-29 15:29:40 +01001412static int via_init(struct hda_codec *codec)
1413{
1414 struct via_spec *spec = codec->spec;
Harald Welte69e52a82008-09-09 15:57:32 +08001415 int i;
1416 for (i = 0; i < spec->num_iverbs; i++)
1417 snd_hda_sequence_write(codec, spec->init_verbs[i]);
1418
Lydia Wang518bf3b2009-10-10 19:07:29 +08001419 spec->codec_type = get_codec_type(codec);
1420 if (spec->codec_type == VT1708BCE)
1421 spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost
1422 same */
Josepch Chanf7278fd2007-12-13 16:40:40 +01001423 /* Lydia Add for EAPD enable */
1424 if (!spec->dig_in_nid) { /* No Digital In connection */
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02001425 if (spec->dig_in_pin) {
1426 snd_hda_codec_write(codec, spec->dig_in_pin, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001427 AC_VERB_SET_PIN_WIDGET_CONTROL,
Takashi Iwai12b74c82008-01-15 12:39:38 +01001428 PIN_OUT);
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02001429 snd_hda_codec_write(codec, spec->dig_in_pin, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001430 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
1431 }
Takashi Iwai12b74c82008-01-15 12:39:38 +01001432 } else /* enable SPDIF-input pin */
1433 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
1434 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
Josepch Chanf7278fd2007-12-13 16:40:40 +01001435
Takashi Iwai9da29272009-05-07 16:31:14 +02001436 /* assign slave outs */
1437 if (spec->slave_dig_outs[0])
1438 codec->slave_dig_outs = spec->slave_dig_outs;
Harald Welte5691ec72008-09-15 22:42:26 +08001439
Joseph Chanc577b8a2006-11-29 15:29:40 +01001440 return 0;
1441}
1442
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001443#ifdef SND_HDA_NEEDS_RESUME
1444static int via_suspend(struct hda_codec *codec, pm_message_t state)
1445{
1446 struct via_spec *spec = codec->spec;
1447 vt1708_stop_hp_work(spec);
1448 return 0;
1449}
1450#endif
1451
Takashi Iwaicb53c622007-08-10 17:21:45 +02001452#ifdef CONFIG_SND_HDA_POWER_SAVE
1453static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1454{
1455 struct via_spec *spec = codec->spec;
1456 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1457}
1458#endif
1459
Joseph Chanc577b8a2006-11-29 15:29:40 +01001460/*
1461 */
1462static struct hda_codec_ops via_patch_ops = {
1463 .build_controls = via_build_controls,
1464 .build_pcms = via_build_pcms,
1465 .init = via_init,
1466 .free = via_free,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001467#ifdef SND_HDA_NEEDS_RESUME
1468 .suspend = via_suspend,
1469#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001470#ifdef CONFIG_SND_HDA_POWER_SAVE
1471 .check_power_status = via_check_power_status,
1472#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001473};
1474
1475/* fill in the dac_nids table from the parsed pin configuration */
1476static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
1477 const struct auto_pin_cfg *cfg)
1478{
1479 int i;
1480 hda_nid_t nid;
1481
1482 spec->multiout.num_dacs = cfg->line_outs;
1483
1484 spec->multiout.dac_nids = spec->private_dac_nids;
1485
1486 for(i = 0; i < 4; i++) {
1487 nid = cfg->line_out_pins[i];
1488 if (nid) {
1489 /* config dac list */
1490 switch (i) {
1491 case AUTO_SEQ_FRONT:
1492 spec->multiout.dac_nids[i] = 0x10;
1493 break;
1494 case AUTO_SEQ_CENLFE:
1495 spec->multiout.dac_nids[i] = 0x12;
1496 break;
1497 case AUTO_SEQ_SURROUND:
Harald Weltefb4cb772008-09-09 15:53:36 +08001498 spec->multiout.dac_nids[i] = 0x11;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001499 break;
1500 case AUTO_SEQ_SIDE:
Harald Weltefb4cb772008-09-09 15:53:36 +08001501 spec->multiout.dac_nids[i] = 0x13;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001502 break;
1503 }
1504 }
1505 }
1506
1507 return 0;
1508}
1509
1510/* add playback controls from the parsed DAC table */
1511static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
1512 const struct auto_pin_cfg *cfg)
1513{
1514 char name[32];
1515 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
Lydia Wang9645c202009-10-10 19:08:27 +08001516 hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
Joseph Chanc577b8a2006-11-29 15:29:40 +01001517 int i, err;
1518
1519 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
1520 nid = cfg->line_out_pins[i];
1521
1522 if (!nid)
1523 continue;
1524
Lydia Wang9645c202009-10-10 19:08:27 +08001525 nid_vol = nid_vols[i];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001526
1527 if (i == AUTO_SEQ_CENLFE) {
1528 /* Center/LFE */
1529 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001530 "Center Playback Volume",
1531 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
1532 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001533 if (err < 0)
1534 return err;
1535 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1536 "LFE Playback Volume",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001537 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
1538 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001539 if (err < 0)
1540 return err;
1541 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1542 "Center Playback Switch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001543 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
1544 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001545 if (err < 0)
1546 return err;
1547 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1548 "LFE Playback Switch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001549 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
1550 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001551 if (err < 0)
1552 return err;
1553 } else if (i == AUTO_SEQ_FRONT){
1554 /* add control to mixer index 0 */
1555 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1556 "Master Front Playback Volume",
Lydia Wang9645c202009-10-10 19:08:27 +08001557 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001558 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001559 if (err < 0)
1560 return err;
1561 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1562 "Master Front Playback Switch",
Lydia Wang9645c202009-10-10 19:08:27 +08001563 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001564 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001565 if (err < 0)
1566 return err;
1567
1568 /* add control to PW3 */
1569 sprintf(name, "%s Playback Volume", chname[i]);
1570 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001571 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1572 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001573 if (err < 0)
1574 return err;
1575 sprintf(name, "%s Playback Switch", chname[i]);
1576 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001577 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1578 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001579 if (err < 0)
1580 return err;
1581 } else {
1582 sprintf(name, "%s Playback Volume", chname[i]);
1583 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001584 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1585 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001586 if (err < 0)
1587 return err;
1588 sprintf(name, "%s Playback Switch", chname[i]);
1589 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001590 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1591 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001592 if (err < 0)
1593 return err;
1594 }
1595 }
1596
1597 return 0;
1598}
1599
Harald Welte0aa62ae2008-09-09 15:58:27 +08001600static void create_hp_imux(struct via_spec *spec)
1601{
1602 int i;
1603 struct hda_input_mux *imux = &spec->private_imux[1];
1604 static const char *texts[] = { "OFF", "ON", NULL};
1605
1606 /* for hp mode select */
1607 i = 0;
1608 while (texts[i] != NULL) {
1609 imux->items[imux->num_items].label = texts[i];
1610 imux->items[imux->num_items].index = i;
1611 imux->num_items++;
1612 i++;
1613 }
1614
1615 spec->hp_mux = &spec->private_imux[1];
1616}
1617
Joseph Chanc577b8a2006-11-29 15:29:40 +01001618static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
1619{
1620 int err;
1621
1622 if (!pin)
1623 return 0;
1624
1625 spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
Lydia Wangcdc17842009-10-10 19:07:47 +08001626 spec->hp_independent_mode_index = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001627
1628 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1629 "Headphone Playback Volume",
1630 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
1631 if (err < 0)
1632 return err;
1633 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1634 "Headphone Playback Switch",
1635 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
1636 if (err < 0)
1637 return err;
1638
Harald Welte0aa62ae2008-09-09 15:58:27 +08001639 create_hp_imux(spec);
1640
Joseph Chanc577b8a2006-11-29 15:29:40 +01001641 return 0;
1642}
1643
1644/* create playback/capture controls for input pins */
1645static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
1646 const struct auto_pin_cfg *cfg)
1647{
1648 static char *labels[] = {
1649 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
1650 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08001651 struct hda_input_mux *imux = &spec->private_imux[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001652 int i, err, idx = 0;
1653
1654 /* for internal loopback recording select */
1655 imux->items[imux->num_items].label = "Stereo Mixer";
1656 imux->items[imux->num_items].index = idx;
1657 imux->num_items++;
1658
1659 for (i = 0; i < AUTO_PIN_LAST; i++) {
1660 if (!cfg->input_pins[i])
1661 continue;
1662
1663 switch (cfg->input_pins[i]) {
1664 case 0x1d: /* Mic */
1665 idx = 2;
1666 break;
1667
1668 case 0x1e: /* Line In */
1669 idx = 3;
1670 break;
1671
1672 case 0x21: /* Front Mic */
1673 idx = 4;
1674 break;
1675
1676 case 0x24: /* CD */
1677 idx = 1;
1678 break;
1679 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08001680 err = via_new_analog_input(spec, labels[i], idx, 0x17);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001681 if (err < 0)
1682 return err;
1683 imux->items[imux->num_items].label = labels[i];
1684 imux->items[imux->num_items].index = idx;
1685 imux->num_items++;
1686 }
1687 return 0;
1688}
1689
Takashi Iwaicb53c622007-08-10 17:21:45 +02001690#ifdef CONFIG_SND_HDA_POWER_SAVE
1691static struct hda_amp_list vt1708_loopbacks[] = {
1692 { 0x17, HDA_INPUT, 1 },
1693 { 0x17, HDA_INPUT, 2 },
1694 { 0x17, HDA_INPUT, 3 },
1695 { 0x17, HDA_INPUT, 4 },
1696 { } /* end */
1697};
1698#endif
1699
Harald Welte76d9b0d2008-09-09 15:50:37 +08001700static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
1701{
1702 unsigned int def_conf;
1703 unsigned char seqassoc;
1704
Takashi Iwai2f334f92009-02-20 14:37:42 +01001705 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08001706 seqassoc = (unsigned char) get_defcfg_association(def_conf);
1707 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08001708 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
1709 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
1710 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
1711 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08001712 }
1713
1714 return;
1715}
1716
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001717static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
1718 struct snd_ctl_elem_value *ucontrol)
1719{
1720 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1721 struct via_spec *spec = codec->spec;
1722
1723 if (spec->codec_type != VT1708)
1724 return 0;
1725 spec->vt1708_jack_detectect =
1726 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
1727 ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
1728 return 0;
1729}
1730
1731static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol,
1732 struct snd_ctl_elem_value *ucontrol)
1733{
1734 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1735 struct via_spec *spec = codec->spec;
1736 int change;
1737
1738 if (spec->codec_type != VT1708)
1739 return 0;
1740 spec->vt1708_jack_detectect = ucontrol->value.integer.value[0];
1741 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
1742 == !spec->vt1708_jack_detectect;
1743 if (spec->vt1708_jack_detectect) {
1744 mute_aa_path(codec, 1);
1745 notify_aa_path_ctls(codec);
1746 }
1747 return change;
1748}
1749
1750static struct snd_kcontrol_new vt1708_jack_detectect[] = {
1751 {
1752 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1753 .name = "Jack Detect",
1754 .count = 1,
1755 .info = snd_ctl_boolean_mono_info,
1756 .get = vt1708_jack_detectect_get,
1757 .put = vt1708_jack_detectect_put,
1758 },
1759 {} /* end */
1760};
1761
Joseph Chanc577b8a2006-11-29 15:29:40 +01001762static int vt1708_parse_auto_config(struct hda_codec *codec)
1763{
1764 struct via_spec *spec = codec->spec;
1765 int err;
1766
Harald Welte76d9b0d2008-09-09 15:50:37 +08001767 /* Add HP and CD pin config connect bit re-config action */
1768 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
1769 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
1770
Joseph Chanc577b8a2006-11-29 15:29:40 +01001771 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
1772 if (err < 0)
1773 return err;
1774 err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
1775 if (err < 0)
1776 return err;
1777 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
1778 return 0; /* can't find valid BIOS pin config */
1779
1780 err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
1781 if (err < 0)
1782 return err;
1783 err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
1784 if (err < 0)
1785 return err;
1786 err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
1787 if (err < 0)
1788 return err;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001789 /* add jack detect on/off control */
1790 err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect);
1791 if (err < 0)
1792 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001793
1794 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
1795
Takashi Iwai0852d7a2009-02-11 11:35:15 +01001796 if (spec->autocfg.dig_outs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001797 spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02001798 spec->dig_in_pin = VT1708_DIGIN_PIN;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001799 if (spec->autocfg.dig_in_pin)
1800 spec->dig_in_nid = VT1708_DIGIN_NID;
1801
Takashi Iwai603c4012008-07-30 15:01:44 +02001802 if (spec->kctls.list)
1803 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001804
Harald Welte69e52a82008-09-09 15:57:32 +08001805 spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001806
Harald Welte0aa62ae2008-09-09 15:58:27 +08001807 spec->input_mux = &spec->private_imux[0];
1808
Harald Weltef8fdd492008-09-15 22:41:31 +08001809 if (spec->hp_mux)
1810 spec->mixers[spec->num_mixers++] = via_hp_mixer;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001811
Lydia Wang1564b282009-10-10 19:07:52 +08001812 spec->mixers[spec->num_mixers++] = via_smart51_mixer;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001813 return 1;
1814}
1815
1816/* init callback for auto-configuration model -- overriding the default init */
1817static int via_auto_init(struct hda_codec *codec)
1818{
1819 via_init(codec);
1820 via_auto_init_multi_out(codec);
1821 via_auto_init_hp_out(codec);
1822 via_auto_init_analog_input(codec);
1823 return 0;
1824}
1825
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001826static void vt1708_update_hp_jack_state(struct work_struct *work)
1827{
1828 struct via_spec *spec = container_of(work, struct via_spec,
1829 vt1708_hp_work.work);
1830 if (spec->codec_type != VT1708)
1831 return;
1832 /* if jack state toggled */
1833 if (spec->vt1708_hp_present
1834 != (snd_hda_codec_read(spec->codec, spec->autocfg.hp_pins[0], 0,
1835 AC_VERB_GET_PIN_SENSE, 0) >> 31)) {
1836 spec->vt1708_hp_present ^= 1;
1837 via_hp_automute(spec->codec);
1838 }
1839 vt1708_start_hp_work(spec);
1840}
1841
Takashi Iwai337b9d02009-07-07 18:18:59 +02001842static int get_mux_nids(struct hda_codec *codec)
1843{
1844 struct via_spec *spec = codec->spec;
1845 hda_nid_t nid, conn[8];
1846 unsigned int type;
1847 int i, n;
1848
1849 for (i = 0; i < spec->num_adc_nids; i++) {
1850 nid = spec->adc_nids[i];
1851 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02001852 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02001853 if (type == AC_WID_PIN)
1854 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02001855 n = snd_hda_get_connections(codec, nid, conn,
1856 ARRAY_SIZE(conn));
1857 if (n <= 0)
1858 break;
1859 if (n > 1) {
1860 spec->mux_nids[i] = nid;
1861 break;
1862 }
1863 nid = conn[0];
1864 }
1865 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02001866 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02001867}
1868
Joseph Chanc577b8a2006-11-29 15:29:40 +01001869static int patch_vt1708(struct hda_codec *codec)
1870{
1871 struct via_spec *spec;
1872 int err;
1873
1874 /* create a codec specific record */
Harald Welteeb14a462008-09-09 15:40:38 +08001875 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001876 if (spec == NULL)
1877 return -ENOMEM;
1878
1879 codec->spec = spec;
1880
1881 /* automatic parse from the BIOS config */
1882 err = vt1708_parse_auto_config(codec);
1883 if (err < 0) {
1884 via_free(codec);
1885 return err;
1886 } else if (!err) {
1887 printk(KERN_INFO "hda_codec: Cannot set up configuration "
1888 "from BIOS. Using genenic mode...\n");
1889 }
1890
1891
1892 spec->stream_name_analog = "VT1708 Analog";
1893 spec->stream_analog_playback = &vt1708_pcm_analog_playback;
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001894 /* disable 32bit format on VT1708 */
1895 if (codec->vendor_id == 0x11061708)
1896 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001897 spec->stream_analog_capture = &vt1708_pcm_analog_capture;
1898
1899 spec->stream_name_digital = "VT1708 Digital";
1900 spec->stream_digital_playback = &vt1708_pcm_digital_playback;
1901 spec->stream_digital_capture = &vt1708_pcm_digital_capture;
1902
1903
1904 if (!spec->adc_nids && spec->input_mux) {
1905 spec->adc_nids = vt1708_adc_nids;
1906 spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
Takashi Iwai0f67a612009-08-31 08:12:29 +02001907 get_mux_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001908 spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
1909 spec->num_mixers++;
1910 }
1911
1912 codec->patch_ops = via_patch_ops;
1913
1914 codec->patch_ops.init = via_auto_init;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001915#ifdef CONFIG_SND_HDA_POWER_SAVE
1916 spec->loopback.amplist = vt1708_loopbacks;
1917#endif
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001918 spec->codec = codec;
1919 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001920 return 0;
1921}
1922
1923/* capture mixer elements */
1924static struct snd_kcontrol_new vt1709_capture_mixer[] = {
1925 HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
1926 HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
1927 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
1928 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
1929 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
1930 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
1931 {
1932 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1933 /* The multiple "Capture Source" controls confuse alsamixer
1934 * So call somewhat different..
Joseph Chanc577b8a2006-11-29 15:29:40 +01001935 */
1936 /* .name = "Capture Source", */
1937 .name = "Input Source",
1938 .count = 1,
1939 .info = via_mux_enum_info,
1940 .get = via_mux_enum_get,
1941 .put = via_mux_enum_put,
1942 },
1943 { } /* end */
1944};
1945
Harald Welte69e52a82008-09-09 15:57:32 +08001946static struct hda_verb vt1709_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08001947 {0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
1948 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08001949 { }
1950};
1951
Joseph Chanc577b8a2006-11-29 15:29:40 +01001952/*
1953 * generic initialization of ADC, input mixers and output mixers
1954 */
1955static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
1956 /*
1957 * Unmute ADC0-2 and set the default input to mic-in
1958 */
1959 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1960 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1961 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1962
1963
Josepch Chanf7278fd2007-12-13 16:40:40 +01001964 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
Joseph Chanc577b8a2006-11-29 15:29:40 +01001965 * mixer widget
1966 */
1967 /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
Josepch Chanf7278fd2007-12-13 16:40:40 +01001968 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1969 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1970 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
1971 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
1972 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
Joseph Chanc577b8a2006-11-29 15:29:40 +01001973
1974 /*
1975 * Set up output selector (0x1a, 0x1b, 0x29)
1976 */
1977 /* set vol=0 to output mixers */
1978 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1979 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1980 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1981
1982 /*
1983 * Unmute PW3 and PW4
1984 */
1985 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1986 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1987
1988 /* Set input of PW4 as AOW4 */
1989 {0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
Joseph Chanc577b8a2006-11-29 15:29:40 +01001990 /* PW9 Output enable */
1991 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
1992 { }
1993};
1994
1995static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
1996 .substreams = 1,
1997 .channels_min = 2,
1998 .channels_max = 10,
1999 .nid = 0x10, /* NID to query formats and rates */
2000 .ops = {
2001 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08002002 .prepare = via_playback_multi_pcm_prepare,
2003 .cleanup = via_playback_multi_pcm_cleanup,
Joseph Chanc577b8a2006-11-29 15:29:40 +01002004 },
2005};
2006
2007static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
2008 .substreams = 1,
2009 .channels_min = 2,
2010 .channels_max = 6,
2011 .nid = 0x10, /* NID to query formats and rates */
2012 .ops = {
2013 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08002014 .prepare = via_playback_multi_pcm_prepare,
2015 .cleanup = via_playback_multi_pcm_cleanup,
Joseph Chanc577b8a2006-11-29 15:29:40 +01002016 },
2017};
2018
2019static struct hda_pcm_stream vt1709_pcm_analog_capture = {
2020 .substreams = 2,
2021 .channels_min = 2,
2022 .channels_max = 2,
2023 .nid = 0x14, /* NID to query formats and rates */
2024 .ops = {
2025 .prepare = via_capture_pcm_prepare,
2026 .cleanup = via_capture_pcm_cleanup
2027 },
2028};
2029
2030static struct hda_pcm_stream vt1709_pcm_digital_playback = {
2031 .substreams = 1,
2032 .channels_min = 2,
2033 .channels_max = 2,
2034 /* NID is set in via_build_pcms */
2035 .ops = {
2036 .open = via_dig_playback_pcm_open,
2037 .close = via_dig_playback_pcm_close
2038 },
2039};
2040
2041static struct hda_pcm_stream vt1709_pcm_digital_capture = {
2042 .substreams = 1,
2043 .channels_min = 2,
2044 .channels_max = 2,
2045};
2046
2047static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
2048 const struct auto_pin_cfg *cfg)
2049{
2050 int i;
2051 hda_nid_t nid;
2052
2053 if (cfg->line_outs == 4) /* 10 channels */
2054 spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
2055 else if (cfg->line_outs == 3) /* 6 channels */
2056 spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
2057
2058 spec->multiout.dac_nids = spec->private_dac_nids;
2059
2060 if (cfg->line_outs == 4) { /* 10 channels */
2061 for (i = 0; i < cfg->line_outs; i++) {
2062 nid = cfg->line_out_pins[i];
2063 if (nid) {
2064 /* config dac list */
2065 switch (i) {
2066 case AUTO_SEQ_FRONT:
2067 /* AOW0 */
2068 spec->multiout.dac_nids[i] = 0x10;
2069 break;
2070 case AUTO_SEQ_CENLFE:
2071 /* AOW2 */
2072 spec->multiout.dac_nids[i] = 0x12;
2073 break;
2074 case AUTO_SEQ_SURROUND:
2075 /* AOW3 */
Harald Weltefb4cb772008-09-09 15:53:36 +08002076 spec->multiout.dac_nids[i] = 0x11;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002077 break;
2078 case AUTO_SEQ_SIDE:
2079 /* AOW1 */
Harald Weltefb4cb772008-09-09 15:53:36 +08002080 spec->multiout.dac_nids[i] = 0x27;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002081 break;
2082 default:
2083 break;
2084 }
2085 }
2086 }
2087 spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
2088
2089 } else if (cfg->line_outs == 3) { /* 6 channels */
2090 for(i = 0; i < cfg->line_outs; i++) {
2091 nid = cfg->line_out_pins[i];
2092 if (nid) {
2093 /* config dac list */
2094 switch(i) {
2095 case AUTO_SEQ_FRONT:
2096 /* AOW0 */
2097 spec->multiout.dac_nids[i] = 0x10;
2098 break;
2099 case AUTO_SEQ_CENLFE:
2100 /* AOW2 */
2101 spec->multiout.dac_nids[i] = 0x12;
2102 break;
2103 case AUTO_SEQ_SURROUND:
2104 /* AOW1 */
2105 spec->multiout.dac_nids[i] = 0x11;
2106 break;
2107 default:
2108 break;
2109 }
2110 }
2111 }
2112 }
2113
2114 return 0;
2115}
2116
2117/* add playback controls from the parsed DAC table */
2118static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
2119 const struct auto_pin_cfg *cfg)
2120{
2121 char name[32];
2122 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
Lydia Wang4483a2f2009-10-10 19:08:29 +08002123 hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
Joseph Chanc577b8a2006-11-29 15:29:40 +01002124 int i, err;
2125
2126 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2127 nid = cfg->line_out_pins[i];
2128
2129 if (!nid)
2130 continue;
2131
Lydia Wang4483a2f2009-10-10 19:08:29 +08002132 nid_vol = nid_vols[i];
2133
Joseph Chanc577b8a2006-11-29 15:29:40 +01002134 if (i == AUTO_SEQ_CENLFE) {
2135 /* Center/LFE */
2136 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2137 "Center Playback Volume",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002138 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002139 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002140 if (err < 0)
2141 return err;
2142 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2143 "LFE Playback Volume",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002144 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002145 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002146 if (err < 0)
2147 return err;
2148 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2149 "Center Playback Switch",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002150 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002151 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002152 if (err < 0)
2153 return err;
2154 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2155 "LFE Playback Switch",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002156 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002157 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002158 if (err < 0)
2159 return err;
2160 } else if (i == AUTO_SEQ_FRONT){
Lydia Wang4483a2f2009-10-10 19:08:29 +08002161 /* ADD control to mixer index 0 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002162 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2163 "Master Front Playback Volume",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002164 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002165 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002166 if (err < 0)
2167 return err;
2168 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2169 "Master Front Playback Switch",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002170 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002171 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002172 if (err < 0)
2173 return err;
2174
2175 /* add control to PW3 */
2176 sprintf(name, "%s Playback Volume", chname[i]);
2177 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002178 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2179 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002180 if (err < 0)
2181 return err;
2182 sprintf(name, "%s Playback Switch", chname[i]);
2183 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002184 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2185 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002186 if (err < 0)
2187 return err;
2188 } else if (i == AUTO_SEQ_SURROUND) {
2189 sprintf(name, "%s Playback Volume", chname[i]);
2190 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002191 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002192 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002193 if (err < 0)
2194 return err;
2195 sprintf(name, "%s Playback Switch", chname[i]);
2196 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002197 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002198 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002199 if (err < 0)
2200 return err;
2201 } else if (i == AUTO_SEQ_SIDE) {
2202 sprintf(name, "%s Playback Volume", chname[i]);
2203 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002204 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002205 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002206 if (err < 0)
2207 return err;
2208 sprintf(name, "%s Playback Switch", chname[i]);
2209 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002210 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002211 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002212 if (err < 0)
2213 return err;
2214 }
2215 }
2216
2217 return 0;
2218}
2219
2220static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2221{
2222 int err;
2223
2224 if (!pin)
2225 return 0;
2226
2227 if (spec->multiout.num_dacs == 5) /* 10 channels */
2228 spec->multiout.hp_nid = VT1709_HP_DAC_NID;
2229 else if (spec->multiout.num_dacs == 3) /* 6 channels */
2230 spec->multiout.hp_nid = 0;
Lydia Wangcdc17842009-10-10 19:07:47 +08002231 spec->hp_independent_mode_index = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002232
2233 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2234 "Headphone Playback Volume",
2235 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2236 if (err < 0)
2237 return err;
2238 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2239 "Headphone Playback Switch",
2240 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2241 if (err < 0)
2242 return err;
2243
2244 return 0;
2245}
2246
2247/* create playback/capture controls for input pins */
2248static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
2249 const struct auto_pin_cfg *cfg)
2250{
2251 static char *labels[] = {
2252 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
2253 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08002254 struct hda_input_mux *imux = &spec->private_imux[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01002255 int i, err, idx = 0;
2256
2257 /* for internal loopback recording select */
2258 imux->items[imux->num_items].label = "Stereo Mixer";
2259 imux->items[imux->num_items].index = idx;
2260 imux->num_items++;
2261
2262 for (i = 0; i < AUTO_PIN_LAST; i++) {
2263 if (!cfg->input_pins[i])
2264 continue;
2265
2266 switch (cfg->input_pins[i]) {
2267 case 0x1d: /* Mic */
2268 idx = 2;
2269 break;
2270
2271 case 0x1e: /* Line In */
2272 idx = 3;
2273 break;
2274
2275 case 0x21: /* Front Mic */
2276 idx = 4;
2277 break;
2278
2279 case 0x23: /* CD */
2280 idx = 1;
2281 break;
2282 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08002283 err = via_new_analog_input(spec, labels[i], idx, 0x18);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002284 if (err < 0)
2285 return err;
2286 imux->items[imux->num_items].label = labels[i];
2287 imux->items[imux->num_items].index = idx;
2288 imux->num_items++;
2289 }
2290 return 0;
2291}
2292
2293static int vt1709_parse_auto_config(struct hda_codec *codec)
2294{
2295 struct via_spec *spec = codec->spec;
2296 int err;
2297
2298 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2299 if (err < 0)
2300 return err;
2301 err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
2302 if (err < 0)
2303 return err;
2304 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2305 return 0; /* can't find valid BIOS pin config */
2306
2307 err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
2308 if (err < 0)
2309 return err;
2310 err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2311 if (err < 0)
2312 return err;
2313 err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg);
2314 if (err < 0)
2315 return err;
2316
2317 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2318
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002319 if (spec->autocfg.dig_outs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002320 spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02002321 spec->dig_in_pin = VT1709_DIGIN_PIN;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002322 if (spec->autocfg.dig_in_pin)
2323 spec->dig_in_nid = VT1709_DIGIN_NID;
2324
Takashi Iwai603c4012008-07-30 15:01:44 +02002325 if (spec->kctls.list)
2326 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002327
Harald Welte0aa62ae2008-09-09 15:58:27 +08002328 spec->input_mux = &spec->private_imux[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01002329
Harald Weltef8fdd492008-09-15 22:41:31 +08002330 if (spec->hp_mux)
2331 spec->mixers[spec->num_mixers++] = via_hp_mixer;
2332
Lydia Wang1564b282009-10-10 19:07:52 +08002333 spec->mixers[spec->num_mixers++] = via_smart51_mixer;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002334 return 1;
2335}
2336
Takashi Iwaicb53c622007-08-10 17:21:45 +02002337#ifdef CONFIG_SND_HDA_POWER_SAVE
2338static struct hda_amp_list vt1709_loopbacks[] = {
2339 { 0x18, HDA_INPUT, 1 },
2340 { 0x18, HDA_INPUT, 2 },
2341 { 0x18, HDA_INPUT, 3 },
2342 { 0x18, HDA_INPUT, 4 },
2343 { } /* end */
2344};
2345#endif
2346
Joseph Chanc577b8a2006-11-29 15:29:40 +01002347static int patch_vt1709_10ch(struct hda_codec *codec)
2348{
2349 struct via_spec *spec;
2350 int err;
2351
2352 /* create a codec specific record */
Harald Welteeb14a462008-09-09 15:40:38 +08002353 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002354 if (spec == NULL)
2355 return -ENOMEM;
2356
2357 codec->spec = spec;
2358
2359 err = vt1709_parse_auto_config(codec);
2360 if (err < 0) {
2361 via_free(codec);
2362 return err;
2363 } else if (!err) {
2364 printk(KERN_INFO "hda_codec: Cannot set up configuration. "
2365 "Using genenic mode...\n");
2366 }
2367
Harald Welte69e52a82008-09-09 15:57:32 +08002368 spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
2369 spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002370
2371 spec->stream_name_analog = "VT1709 Analog";
2372 spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
2373 spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2374
2375 spec->stream_name_digital = "VT1709 Digital";
2376 spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2377 spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2378
2379
2380 if (!spec->adc_nids && spec->input_mux) {
2381 spec->adc_nids = vt1709_adc_nids;
2382 spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02002383 get_mux_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002384 spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2385 spec->num_mixers++;
2386 }
2387
2388 codec->patch_ops = via_patch_ops;
2389
2390 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08002391 codec->patch_ops.unsol_event = via_unsol_event;
Takashi Iwaicb53c622007-08-10 17:21:45 +02002392#ifdef CONFIG_SND_HDA_POWER_SAVE
2393 spec->loopback.amplist = vt1709_loopbacks;
2394#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01002395
2396 return 0;
2397}
2398/*
2399 * generic initialization of ADC, input mixers and output mixers
2400 */
2401static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
2402 /*
2403 * Unmute ADC0-2 and set the default input to mic-in
2404 */
2405 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2406 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2407 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2408
2409
2410 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2411 * mixer widget
2412 */
2413 /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2414 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2415 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2416 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2417 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2418 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2419
2420 /*
2421 * Set up output selector (0x1a, 0x1b, 0x29)
2422 */
2423 /* set vol=0 to output mixers */
2424 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2425 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2426 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2427
2428 /*
2429 * Unmute PW3 and PW4
2430 */
2431 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2432 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2433
2434 /* Set input of PW4 as MW0 */
2435 {0x20, AC_VERB_SET_CONNECT_SEL, 0},
Joseph Chanc577b8a2006-11-29 15:29:40 +01002436 /* PW9 Output enable */
2437 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2438 { }
2439};
2440
2441static int patch_vt1709_6ch(struct hda_codec *codec)
2442{
2443 struct via_spec *spec;
2444 int err;
2445
2446 /* create a codec specific record */
Harald Welteeb14a462008-09-09 15:40:38 +08002447 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002448 if (spec == NULL)
2449 return -ENOMEM;
2450
2451 codec->spec = spec;
2452
2453 err = vt1709_parse_auto_config(codec);
2454 if (err < 0) {
2455 via_free(codec);
2456 return err;
2457 } else if (!err) {
2458 printk(KERN_INFO "hda_codec: Cannot set up configuration. "
2459 "Using genenic mode...\n");
2460 }
2461
Harald Welte69e52a82008-09-09 15:57:32 +08002462 spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
2463 spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002464
2465 spec->stream_name_analog = "VT1709 Analog";
2466 spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
2467 spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2468
2469 spec->stream_name_digital = "VT1709 Digital";
2470 spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2471 spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2472
2473
2474 if (!spec->adc_nids && spec->input_mux) {
2475 spec->adc_nids = vt1709_adc_nids;
2476 spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02002477 get_mux_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002478 spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2479 spec->num_mixers++;
2480 }
2481
2482 codec->patch_ops = via_patch_ops;
2483
2484 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08002485 codec->patch_ops.unsol_event = via_unsol_event;
Takashi Iwaicb53c622007-08-10 17:21:45 +02002486#ifdef CONFIG_SND_HDA_POWER_SAVE
2487 spec->loopback.amplist = vt1709_loopbacks;
2488#endif
Josepch Chanf7278fd2007-12-13 16:40:40 +01002489 return 0;
2490}
2491
2492/* capture mixer elements */
2493static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
2494 HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
2495 HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
2496 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
2497 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
2498 {
2499 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2500 /* The multiple "Capture Source" controls confuse alsamixer
2501 * So call somewhat different..
Josepch Chanf7278fd2007-12-13 16:40:40 +01002502 */
2503 /* .name = "Capture Source", */
2504 .name = "Input Source",
2505 .count = 1,
2506 .info = via_mux_enum_info,
2507 .get = via_mux_enum_get,
2508 .put = via_mux_enum_put,
2509 },
2510 { } /* end */
2511};
2512/*
2513 * generic initialization of ADC, input mixers and output mixers
2514 */
2515static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
2516 /*
2517 * Unmute ADC0-1 and set the default input to mic-in
2518 */
2519 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2520 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2521
2522
2523 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2524 * mixer widget
2525 */
2526 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2527 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2528 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2529 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2530 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2531 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2532
2533 /*
2534 * Set up output mixers
2535 */
2536 /* set vol=0 to output mixers */
2537 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2538 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2539 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2540
2541 /* Setup default input to PW4 */
2542 {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1},
2543 /* PW9 Output enable */
2544 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2545 /* PW10 Input enable */
2546 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2547 { }
2548};
2549
2550static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
2551 /*
2552 * Unmute ADC0-1 and set the default input to mic-in
2553 */
2554 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2555 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2556
2557
2558 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2559 * mixer widget
2560 */
2561 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2562 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2563 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2564 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2565 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2566 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2567
2568 /*
2569 * Set up output mixers
2570 */
2571 /* set vol=0 to output mixers */
2572 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2573 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2574 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2575
2576 /* Setup default input of PW4 to MW0 */
2577 {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
2578 /* PW9 Output enable */
2579 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2580 /* PW10 Input enable */
2581 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2582 { }
2583};
2584
Harald Welte69e52a82008-09-09 15:57:32 +08002585static struct hda_verb vt1708B_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08002586 {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
2587 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
2588 {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2589 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2590 {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2591 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2592 {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2593 {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2594 {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08002595 { }
2596};
2597
Lydia Wang17314372009-10-10 19:07:37 +08002598static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
2599 struct hda_codec *codec,
2600 struct snd_pcm_substream *substream)
2601{
2602 int idle = substream->pstr->substream_opened == 1
2603 && substream->ref_count == 0;
2604
2605 analog_low_current_mode(codec, idle);
2606 return 0;
2607}
2608
Josepch Chanf7278fd2007-12-13 16:40:40 +01002609static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08002610 .substreams = 2,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002611 .channels_min = 2,
2612 .channels_max = 8,
2613 .nid = 0x10, /* NID to query formats and rates */
2614 .ops = {
2615 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08002616 .prepare = via_playback_multi_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08002617 .cleanup = via_playback_multi_pcm_cleanup,
2618 .close = via_pcm_open_close
Josepch Chanf7278fd2007-12-13 16:40:40 +01002619 },
2620};
2621
2622static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08002623 .substreams = 2,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002624 .channels_min = 2,
2625 .channels_max = 4,
2626 .nid = 0x10, /* NID to query formats and rates */
2627 .ops = {
2628 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08002629 .prepare = via_playback_multi_pcm_prepare,
2630 .cleanup = via_playback_multi_pcm_cleanup
Josepch Chanf7278fd2007-12-13 16:40:40 +01002631 },
2632};
2633
2634static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
2635 .substreams = 2,
2636 .channels_min = 2,
2637 .channels_max = 2,
2638 .nid = 0x13, /* NID to query formats and rates */
2639 .ops = {
Lydia Wang17314372009-10-10 19:07:37 +08002640 .open = via_pcm_open_close,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002641 .prepare = via_capture_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08002642 .cleanup = via_capture_pcm_cleanup,
2643 .close = via_pcm_open_close
Josepch Chanf7278fd2007-12-13 16:40:40 +01002644 },
2645};
2646
2647static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
2648 .substreams = 1,
2649 .channels_min = 2,
2650 .channels_max = 2,
2651 /* NID is set in via_build_pcms */
2652 .ops = {
2653 .open = via_dig_playback_pcm_open,
2654 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02002655 .prepare = via_dig_playback_pcm_prepare,
2656 .cleanup = via_dig_playback_pcm_cleanup
Josepch Chanf7278fd2007-12-13 16:40:40 +01002657 },
2658};
2659
2660static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
2661 .substreams = 1,
2662 .channels_min = 2,
2663 .channels_max = 2,
2664};
2665
2666/* fill in the dac_nids table from the parsed pin configuration */
2667static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
2668 const struct auto_pin_cfg *cfg)
2669{
2670 int i;
2671 hda_nid_t nid;
2672
2673 spec->multiout.num_dacs = cfg->line_outs;
2674
2675 spec->multiout.dac_nids = spec->private_dac_nids;
2676
2677 for (i = 0; i < 4; i++) {
2678 nid = cfg->line_out_pins[i];
2679 if (nid) {
2680 /* config dac list */
2681 switch (i) {
2682 case AUTO_SEQ_FRONT:
2683 spec->multiout.dac_nids[i] = 0x10;
2684 break;
2685 case AUTO_SEQ_CENLFE:
2686 spec->multiout.dac_nids[i] = 0x24;
2687 break;
2688 case AUTO_SEQ_SURROUND:
Harald Weltefb4cb772008-09-09 15:53:36 +08002689 spec->multiout.dac_nids[i] = 0x11;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002690 break;
2691 case AUTO_SEQ_SIDE:
Harald Weltefb4cb772008-09-09 15:53:36 +08002692 spec->multiout.dac_nids[i] = 0x25;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002693 break;
2694 }
2695 }
2696 }
2697
2698 return 0;
2699}
2700
2701/* add playback controls from the parsed DAC table */
2702static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
2703 const struct auto_pin_cfg *cfg)
2704{
2705 char name[32];
2706 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
Harald Weltefb4cb772008-09-09 15:53:36 +08002707 hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
Josepch Chanf7278fd2007-12-13 16:40:40 +01002708 hda_nid_t nid, nid_vol = 0;
2709 int i, err;
2710
2711 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2712 nid = cfg->line_out_pins[i];
2713
2714 if (!nid)
2715 continue;
2716
2717 nid_vol = nid_vols[i];
2718
2719 if (i == AUTO_SEQ_CENLFE) {
2720 /* Center/LFE */
2721 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2722 "Center Playback Volume",
2723 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2724 HDA_OUTPUT));
2725 if (err < 0)
2726 return err;
2727 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2728 "LFE Playback Volume",
2729 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2730 HDA_OUTPUT));
2731 if (err < 0)
2732 return err;
2733 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2734 "Center Playback Switch",
2735 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2736 HDA_OUTPUT));
2737 if (err < 0)
2738 return err;
2739 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2740 "LFE Playback Switch",
2741 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2742 HDA_OUTPUT));
2743 if (err < 0)
2744 return err;
2745 } else if (i == AUTO_SEQ_FRONT) {
2746 /* add control to mixer index 0 */
2747 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2748 "Master Front Playback Volume",
2749 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2750 HDA_INPUT));
2751 if (err < 0)
2752 return err;
2753 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2754 "Master Front Playback Switch",
2755 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2756 HDA_INPUT));
2757 if (err < 0)
2758 return err;
2759
2760 /* add control to PW3 */
2761 sprintf(name, "%s Playback Volume", chname[i]);
2762 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2763 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2764 HDA_OUTPUT));
2765 if (err < 0)
2766 return err;
2767 sprintf(name, "%s Playback Switch", chname[i]);
2768 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2769 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2770 HDA_OUTPUT));
2771 if (err < 0)
2772 return err;
2773 } else {
2774 sprintf(name, "%s Playback Volume", chname[i]);
2775 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2776 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2777 HDA_OUTPUT));
2778 if (err < 0)
2779 return err;
2780 sprintf(name, "%s Playback Switch", chname[i]);
2781 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2782 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2783 HDA_OUTPUT));
2784 if (err < 0)
2785 return err;
2786 }
2787 }
2788
2789 return 0;
2790}
2791
2792static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2793{
2794 int err;
2795
2796 if (!pin)
2797 return 0;
2798
2799 spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
Lydia Wangcdc17842009-10-10 19:07:47 +08002800 spec->hp_independent_mode_index = 1;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002801
2802 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2803 "Headphone Playback Volume",
2804 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2805 if (err < 0)
2806 return err;
2807 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2808 "Headphone Playback Switch",
2809 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2810 if (err < 0)
2811 return err;
2812
Harald Welte0aa62ae2008-09-09 15:58:27 +08002813 create_hp_imux(spec);
2814
Josepch Chanf7278fd2007-12-13 16:40:40 +01002815 return 0;
2816}
2817
2818/* create playback/capture controls for input pins */
2819static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
2820 const struct auto_pin_cfg *cfg)
2821{
2822 static char *labels[] = {
2823 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
2824 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08002825 struct hda_input_mux *imux = &spec->private_imux[0];
Josepch Chanf7278fd2007-12-13 16:40:40 +01002826 int i, err, idx = 0;
2827
2828 /* for internal loopback recording select */
2829 imux->items[imux->num_items].label = "Stereo Mixer";
2830 imux->items[imux->num_items].index = idx;
2831 imux->num_items++;
2832
2833 for (i = 0; i < AUTO_PIN_LAST; i++) {
2834 if (!cfg->input_pins[i])
2835 continue;
2836
2837 switch (cfg->input_pins[i]) {
2838 case 0x1a: /* Mic */
2839 idx = 2;
2840 break;
2841
2842 case 0x1b: /* Line In */
2843 idx = 3;
2844 break;
2845
2846 case 0x1e: /* Front Mic */
2847 idx = 4;
2848 break;
2849
2850 case 0x1f: /* CD */
2851 idx = 1;
2852 break;
2853 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08002854 err = via_new_analog_input(spec, labels[i], idx, 0x16);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002855 if (err < 0)
2856 return err;
2857 imux->items[imux->num_items].label = labels[i];
2858 imux->items[imux->num_items].index = idx;
2859 imux->num_items++;
2860 }
2861 return 0;
2862}
2863
2864static int vt1708B_parse_auto_config(struct hda_codec *codec)
2865{
2866 struct via_spec *spec = codec->spec;
2867 int err;
2868
2869 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2870 if (err < 0)
2871 return err;
2872 err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
2873 if (err < 0)
2874 return err;
2875 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2876 return 0; /* can't find valid BIOS pin config */
2877
2878 err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
2879 if (err < 0)
2880 return err;
2881 err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2882 if (err < 0)
2883 return err;
2884 err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
2885 if (err < 0)
2886 return err;
2887
2888 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2889
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002890 if (spec->autocfg.dig_outs)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002891 spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02002892 spec->dig_in_pin = VT1708B_DIGIN_PIN;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002893 if (spec->autocfg.dig_in_pin)
2894 spec->dig_in_nid = VT1708B_DIGIN_NID;
2895
Takashi Iwai603c4012008-07-30 15:01:44 +02002896 if (spec->kctls.list)
2897 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002898
Harald Welte0aa62ae2008-09-09 15:58:27 +08002899 spec->input_mux = &spec->private_imux[0];
2900
Harald Weltef8fdd492008-09-15 22:41:31 +08002901 if (spec->hp_mux)
2902 spec->mixers[spec->num_mixers++] = via_hp_mixer;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002903
Lydia Wang1564b282009-10-10 19:07:52 +08002904 spec->mixers[spec->num_mixers++] = via_smart51_mixer;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002905 return 1;
2906}
2907
2908#ifdef CONFIG_SND_HDA_POWER_SAVE
2909static struct hda_amp_list vt1708B_loopbacks[] = {
2910 { 0x16, HDA_INPUT, 1 },
2911 { 0x16, HDA_INPUT, 2 },
2912 { 0x16, HDA_INPUT, 3 },
2913 { 0x16, HDA_INPUT, 4 },
2914 { } /* end */
2915};
2916#endif
Lydia Wang518bf3b2009-10-10 19:07:29 +08002917static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002918static int patch_vt1708B_8ch(struct hda_codec *codec)
2919{
2920 struct via_spec *spec;
2921 int err;
2922
Lydia Wang518bf3b2009-10-10 19:07:29 +08002923 if (get_codec_type(codec) == VT1708BCE)
2924 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002925 /* create a codec specific record */
Harald Welteeb14a462008-09-09 15:40:38 +08002926 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002927 if (spec == NULL)
2928 return -ENOMEM;
2929
2930 codec->spec = spec;
2931
2932 /* automatic parse from the BIOS config */
2933 err = vt1708B_parse_auto_config(codec);
2934 if (err < 0) {
2935 via_free(codec);
2936 return err;
2937 } else if (!err) {
2938 printk(KERN_INFO "hda_codec: Cannot set up configuration "
2939 "from BIOS. Using genenic mode...\n");
2940 }
2941
Harald Welte69e52a82008-09-09 15:57:32 +08002942 spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
2943 spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002944
2945 spec->stream_name_analog = "VT1708B Analog";
2946 spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
2947 spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
2948
2949 spec->stream_name_digital = "VT1708B Digital";
2950 spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
2951 spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
2952
2953 if (!spec->adc_nids && spec->input_mux) {
2954 spec->adc_nids = vt1708B_adc_nids;
2955 spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02002956 get_mux_nids(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002957 spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
2958 spec->num_mixers++;
2959 }
2960
2961 codec->patch_ops = via_patch_ops;
2962
2963 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08002964 codec->patch_ops.unsol_event = via_unsol_event;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002965#ifdef CONFIG_SND_HDA_POWER_SAVE
2966 spec->loopback.amplist = vt1708B_loopbacks;
2967#endif
2968
2969 return 0;
2970}
2971
2972static int patch_vt1708B_4ch(struct hda_codec *codec)
2973{
2974 struct via_spec *spec;
2975 int err;
2976
2977 /* create a codec specific record */
Harald Welteeb14a462008-09-09 15:40:38 +08002978 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002979 if (spec == NULL)
2980 return -ENOMEM;
2981
2982 codec->spec = spec;
2983
2984 /* automatic parse from the BIOS config */
2985 err = vt1708B_parse_auto_config(codec);
2986 if (err < 0) {
2987 via_free(codec);
2988 return err;
2989 } else if (!err) {
2990 printk(KERN_INFO "hda_codec: Cannot set up configuration "
2991 "from BIOS. Using genenic mode...\n");
2992 }
2993
Harald Welte69e52a82008-09-09 15:57:32 +08002994 spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
2995 spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002996
2997 spec->stream_name_analog = "VT1708B Analog";
2998 spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
2999 spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3000
3001 spec->stream_name_digital = "VT1708B Digital";
3002 spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3003 spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3004
3005 if (!spec->adc_nids && spec->input_mux) {
3006 spec->adc_nids = vt1708B_adc_nids;
3007 spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003008 get_mux_nids(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003009 spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3010 spec->num_mixers++;
3011 }
3012
3013 codec->patch_ops = via_patch_ops;
3014
3015 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003016 codec->patch_ops.unsol_event = via_unsol_event;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003017#ifdef CONFIG_SND_HDA_POWER_SAVE
3018 spec->loopback.amplist = vt1708B_loopbacks;
3019#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01003020
3021 return 0;
3022}
3023
Harald Welted949cac2008-09-09 15:56:01 +08003024/* Patch for VT1708S */
3025
3026/* capture mixer elements */
3027static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
3028 HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3029 HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3030 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3031 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
Lydia Wang6369bcf2009-10-10 19:08:31 +08003032 HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
3033 HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
3034 HDA_INPUT),
Harald Welted949cac2008-09-09 15:56:01 +08003035 {
3036 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3037 /* The multiple "Capture Source" controls confuse alsamixer
3038 * So call somewhat different..
3039 */
3040 /* .name = "Capture Source", */
3041 .name = "Input Source",
3042 .count = 1,
3043 .info = via_mux_enum_info,
3044 .get = via_mux_enum_get,
3045 .put = via_mux_enum_put,
3046 },
3047 { } /* end */
3048};
3049
3050static struct hda_verb vt1708S_volume_init_verbs[] = {
3051 /* Unmute ADC0-1 and set the default input to mic-in */
3052 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3053 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3054
3055 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
3056 * analog-loopback mixer widget */
3057 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3058 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3059 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3060 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3061 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3062 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3063
3064 /* Setup default input of PW4 to MW0 */
3065 {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
Harald Welte5691ec72008-09-15 22:42:26 +08003066 /* PW9, PW10 Output enable */
Harald Welted949cac2008-09-09 15:56:01 +08003067 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Harald Welte5691ec72008-09-15 22:42:26 +08003068 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Harald Welted7426322008-09-15 22:43:23 +08003069 /* Enable Mic Boost Volume backdoor */
3070 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003071 /* don't bybass mixer */
3072 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08003073 { }
3074};
3075
Harald Welte69e52a82008-09-09 15:57:32 +08003076static struct hda_verb vt1708S_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08003077 {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3078 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3079 {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3080 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3081 {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3082 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3083 {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3084 {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3085 {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08003086 { }
3087};
3088
Harald Welted949cac2008-09-09 15:56:01 +08003089static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
3090 .substreams = 2,
3091 .channels_min = 2,
3092 .channels_max = 8,
3093 .nid = 0x10, /* NID to query formats and rates */
3094 .ops = {
3095 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08003096 .prepare = via_playback_multi_pcm_prepare,
3097 .cleanup = via_playback_multi_pcm_cleanup,
Lydia Wang17314372009-10-10 19:07:37 +08003098 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003099 },
3100};
3101
3102static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
3103 .substreams = 2,
3104 .channels_min = 2,
3105 .channels_max = 2,
3106 .nid = 0x13, /* NID to query formats and rates */
3107 .ops = {
Lydia Wang17314372009-10-10 19:07:37 +08003108 .open = via_pcm_open_close,
Harald Welted949cac2008-09-09 15:56:01 +08003109 .prepare = via_capture_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08003110 .cleanup = via_capture_pcm_cleanup,
3111 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003112 },
3113};
3114
3115static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
Takashi Iwai9da29272009-05-07 16:31:14 +02003116 .substreams = 1,
Harald Welted949cac2008-09-09 15:56:01 +08003117 .channels_min = 2,
3118 .channels_max = 2,
3119 /* NID is set in via_build_pcms */
3120 .ops = {
3121 .open = via_dig_playback_pcm_open,
3122 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02003123 .prepare = via_dig_playback_pcm_prepare,
3124 .cleanup = via_dig_playback_pcm_cleanup
Harald Welted949cac2008-09-09 15:56:01 +08003125 },
3126};
3127
3128/* fill in the dac_nids table from the parsed pin configuration */
3129static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
3130 const struct auto_pin_cfg *cfg)
3131{
3132 int i;
3133 hda_nid_t nid;
3134
3135 spec->multiout.num_dacs = cfg->line_outs;
3136
3137 spec->multiout.dac_nids = spec->private_dac_nids;
3138
3139 for (i = 0; i < 4; i++) {
3140 nid = cfg->line_out_pins[i];
3141 if (nid) {
3142 /* config dac list */
3143 switch (i) {
3144 case AUTO_SEQ_FRONT:
3145 spec->multiout.dac_nids[i] = 0x10;
3146 break;
3147 case AUTO_SEQ_CENLFE:
3148 spec->multiout.dac_nids[i] = 0x24;
3149 break;
3150 case AUTO_SEQ_SURROUND:
3151 spec->multiout.dac_nids[i] = 0x11;
3152 break;
3153 case AUTO_SEQ_SIDE:
3154 spec->multiout.dac_nids[i] = 0x25;
3155 break;
3156 }
3157 }
3158 }
3159
3160 return 0;
3161}
3162
3163/* add playback controls from the parsed DAC table */
3164static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
3165 const struct auto_pin_cfg *cfg)
3166{
3167 char name[32];
3168 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
3169 hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25};
3170 hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27};
3171 hda_nid_t nid, nid_vol, nid_mute;
3172 int i, err;
3173
3174 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3175 nid = cfg->line_out_pins[i];
3176
3177 if (!nid)
3178 continue;
3179
3180 nid_vol = nid_vols[i];
3181 nid_mute = nid_mutes[i];
3182
3183 if (i == AUTO_SEQ_CENLFE) {
3184 /* Center/LFE */
3185 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3186 "Center Playback Volume",
3187 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3188 HDA_OUTPUT));
3189 if (err < 0)
3190 return err;
3191 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3192 "LFE Playback Volume",
3193 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3194 HDA_OUTPUT));
3195 if (err < 0)
3196 return err;
3197 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3198 "Center Playback Switch",
3199 HDA_COMPOSE_AMP_VAL(nid_mute,
3200 1, 0,
3201 HDA_OUTPUT));
3202 if (err < 0)
3203 return err;
3204 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3205 "LFE Playback Switch",
3206 HDA_COMPOSE_AMP_VAL(nid_mute,
3207 2, 0,
3208 HDA_OUTPUT));
3209 if (err < 0)
3210 return err;
3211 } else if (i == AUTO_SEQ_FRONT) {
3212 /* add control to mixer index 0 */
3213 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3214 "Master Front Playback Volume",
3215 HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3216 HDA_INPUT));
3217 if (err < 0)
3218 return err;
3219 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3220 "Master Front Playback Switch",
3221 HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3222 HDA_INPUT));
3223 if (err < 0)
3224 return err;
3225
3226 /* Front */
3227 sprintf(name, "%s Playback Volume", chname[i]);
3228 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3229 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3230 HDA_OUTPUT));
3231 if (err < 0)
3232 return err;
3233 sprintf(name, "%s Playback Switch", chname[i]);
3234 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3235 HDA_COMPOSE_AMP_VAL(nid_mute,
3236 3, 0,
3237 HDA_OUTPUT));
3238 if (err < 0)
3239 return err;
3240 } else {
3241 sprintf(name, "%s Playback Volume", chname[i]);
3242 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3243 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3244 HDA_OUTPUT));
3245 if (err < 0)
3246 return err;
3247 sprintf(name, "%s Playback Switch", chname[i]);
3248 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3249 HDA_COMPOSE_AMP_VAL(nid_mute,
3250 3, 0,
3251 HDA_OUTPUT));
3252 if (err < 0)
3253 return err;
3254 }
3255 }
3256
3257 return 0;
3258}
3259
3260static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3261{
3262 int err;
3263
3264 if (!pin)
3265 return 0;
3266
3267 spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
Lydia Wangcdc17842009-10-10 19:07:47 +08003268 spec->hp_independent_mode_index = 1;
Harald Welted949cac2008-09-09 15:56:01 +08003269
3270 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3271 "Headphone Playback Volume",
3272 HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
3273 if (err < 0)
3274 return err;
3275
3276 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3277 "Headphone Playback Switch",
3278 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3279 if (err < 0)
3280 return err;
3281
Harald Welte0aa62ae2008-09-09 15:58:27 +08003282 create_hp_imux(spec);
3283
Harald Welted949cac2008-09-09 15:56:01 +08003284 return 0;
3285}
3286
3287/* create playback/capture controls for input pins */
3288static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
3289 const struct auto_pin_cfg *cfg)
3290{
3291 static char *labels[] = {
3292 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
3293 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08003294 struct hda_input_mux *imux = &spec->private_imux[0];
Harald Welted949cac2008-09-09 15:56:01 +08003295 int i, err, idx = 0;
3296
3297 /* for internal loopback recording select */
3298 imux->items[imux->num_items].label = "Stereo Mixer";
3299 imux->items[imux->num_items].index = 5;
3300 imux->num_items++;
3301
3302 for (i = 0; i < AUTO_PIN_LAST; i++) {
3303 if (!cfg->input_pins[i])
3304 continue;
3305
3306 switch (cfg->input_pins[i]) {
3307 case 0x1a: /* Mic */
3308 idx = 2;
3309 break;
3310
3311 case 0x1b: /* Line In */
3312 idx = 3;
3313 break;
3314
3315 case 0x1e: /* Front Mic */
3316 idx = 4;
3317 break;
3318
3319 case 0x1f: /* CD */
3320 idx = 1;
3321 break;
3322 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08003323 err = via_new_analog_input(spec, labels[i], idx, 0x16);
Harald Welted949cac2008-09-09 15:56:01 +08003324 if (err < 0)
3325 return err;
3326 imux->items[imux->num_items].label = labels[i];
3327 imux->items[imux->num_items].index = idx-1;
3328 imux->num_items++;
3329 }
3330 return 0;
3331}
3332
Takashi Iwai9da29272009-05-07 16:31:14 +02003333/* fill out digital output widgets; one for master and one for slave outputs */
3334static void fill_dig_outs(struct hda_codec *codec)
3335{
3336 struct via_spec *spec = codec->spec;
3337 int i;
3338
3339 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3340 hda_nid_t nid;
3341 int conn;
3342
3343 nid = spec->autocfg.dig_out_pins[i];
3344 if (!nid)
3345 continue;
3346 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3347 if (conn < 1)
3348 continue;
3349 if (!spec->multiout.dig_out_nid)
3350 spec->multiout.dig_out_nid = nid;
3351 else {
3352 spec->slave_dig_outs[0] = nid;
3353 break; /* at most two dig outs */
3354 }
3355 }
3356}
3357
Harald Welted949cac2008-09-09 15:56:01 +08003358static int vt1708S_parse_auto_config(struct hda_codec *codec)
3359{
3360 struct via_spec *spec = codec->spec;
3361 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003362
Takashi Iwai9da29272009-05-07 16:31:14 +02003363 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
Harald Welted949cac2008-09-09 15:56:01 +08003364 if (err < 0)
3365 return err;
3366 err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
3367 if (err < 0)
3368 return err;
3369 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3370 return 0; /* can't find valid BIOS pin config */
3371
3372 err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg);
3373 if (err < 0)
3374 return err;
3375 err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3376 if (err < 0)
3377 return err;
3378 err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg);
3379 if (err < 0)
3380 return err;
3381
3382 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3383
Takashi Iwai9da29272009-05-07 16:31:14 +02003384 fill_dig_outs(codec);
Harald Welte98aa34c2008-09-09 16:02:09 +08003385
Takashi Iwai603c4012008-07-30 15:01:44 +02003386 if (spec->kctls.list)
3387 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Harald Welted949cac2008-09-09 15:56:01 +08003388
Harald Welte0aa62ae2008-09-09 15:58:27 +08003389 spec->input_mux = &spec->private_imux[0];
3390
Harald Weltef8fdd492008-09-15 22:41:31 +08003391 if (spec->hp_mux)
3392 spec->mixers[spec->num_mixers++] = via_hp_mixer;
Harald Welted949cac2008-09-09 15:56:01 +08003393
Lydia Wang1564b282009-10-10 19:07:52 +08003394 spec->mixers[spec->num_mixers++] = via_smart51_mixer;
Harald Welted949cac2008-09-09 15:56:01 +08003395 return 1;
3396}
3397
3398#ifdef CONFIG_SND_HDA_POWER_SAVE
3399static struct hda_amp_list vt1708S_loopbacks[] = {
3400 { 0x16, HDA_INPUT, 1 },
3401 { 0x16, HDA_INPUT, 2 },
3402 { 0x16, HDA_INPUT, 3 },
3403 { 0x16, HDA_INPUT, 4 },
3404 { } /* end */
3405};
3406#endif
3407
Lydia Wang6369bcf2009-10-10 19:08:31 +08003408static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3409 int offset, int num_steps, int step_size)
3410{
3411 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3412 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3413 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3414 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3415 (0 << AC_AMPCAP_MUTE_SHIFT));
3416}
3417
Harald Welted949cac2008-09-09 15:56:01 +08003418static int patch_vt1708S(struct hda_codec *codec)
3419{
3420 struct via_spec *spec;
3421 int err;
3422
3423 /* create a codec specific record */
3424 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3425 if (spec == NULL)
3426 return -ENOMEM;
3427
3428 codec->spec = spec;
3429
3430 /* automatic parse from the BIOS config */
3431 err = vt1708S_parse_auto_config(codec);
3432 if (err < 0) {
3433 via_free(codec);
3434 return err;
3435 } else if (!err) {
3436 printk(KERN_INFO "hda_codec: Cannot set up configuration "
3437 "from BIOS. Using genenic mode...\n");
3438 }
3439
Harald Welte69e52a82008-09-09 15:57:32 +08003440 spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
3441 spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003442
3443 spec->stream_name_analog = "VT1708S Analog";
3444 spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
3445 spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
3446
3447 spec->stream_name_digital = "VT1708S Digital";
3448 spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
3449
3450 if (!spec->adc_nids && spec->input_mux) {
3451 spec->adc_nids = vt1708S_adc_nids;
3452 spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003453 get_mux_nids(codec);
Lydia Wang6369bcf2009-10-10 19:08:31 +08003454 override_mic_boost(codec, 0x1a, 0, 3, 40);
3455 override_mic_boost(codec, 0x1e, 0, 3, 40);
Harald Welted949cac2008-09-09 15:56:01 +08003456 spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
3457 spec->num_mixers++;
3458 }
3459
3460 codec->patch_ops = via_patch_ops;
3461
3462 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003463 codec->patch_ops.unsol_event = via_unsol_event;
Harald Welted949cac2008-09-09 15:56:01 +08003464#ifdef CONFIG_SND_HDA_POWER_SAVE
3465 spec->loopback.amplist = vt1708S_loopbacks;
3466#endif
3467
Lydia Wang518bf3b2009-10-10 19:07:29 +08003468 /* correct names for VT1708BCE */
3469 if (get_codec_type(codec) == VT1708BCE) {
3470 kfree(codec->chip_name);
3471 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3472 snprintf(codec->bus->card->mixername,
3473 sizeof(codec->bus->card->mixername),
3474 "%s %s", codec->vendor_name, codec->chip_name);
3475 spec->stream_name_analog = "VT1708BCE Analog";
3476 spec->stream_name_digital = "VT1708BCE Digital";
3477 }
Harald Welted949cac2008-09-09 15:56:01 +08003478 return 0;
3479}
3480
3481/* Patch for VT1702 */
3482
3483/* capture mixer elements */
3484static struct snd_kcontrol_new vt1702_capture_mixer[] = {
3485 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
3486 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
3487 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
3488 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
3489 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
3490 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
3491 HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
3492 HDA_INPUT),
3493 {
3494 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3495 /* The multiple "Capture Source" controls confuse alsamixer
3496 * So call somewhat different..
3497 */
3498 /* .name = "Capture Source", */
3499 .name = "Input Source",
3500 .count = 1,
3501 .info = via_mux_enum_info,
3502 .get = via_mux_enum_get,
3503 .put = via_mux_enum_put,
3504 },
3505 { } /* end */
3506};
3507
3508static struct hda_verb vt1702_volume_init_verbs[] = {
3509 /*
3510 * Unmute ADC0-1 and set the default input to mic-in
3511 */
3512 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3513 {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3514 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3515
3516
3517 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3518 * mixer widget
3519 */
3520 /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
3521 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3522 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3523 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3524 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3525 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3526
3527 /* Setup default input of PW4 to MW0 */
3528 {0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
3529 /* PW6 PW7 Output enable */
3530 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3531 {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003532 /* mixer enable */
3533 {0x1, 0xF88, 0x3},
3534 /* GPIO 0~2 */
3535 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003536 { }
3537};
3538
Harald Welte69e52a82008-09-09 15:57:32 +08003539static struct hda_verb vt1702_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08003540 {0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
3541 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3542 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3543 {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3544 {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3545 {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08003546 { }
3547};
3548
Harald Welted949cac2008-09-09 15:56:01 +08003549static struct hda_pcm_stream vt1702_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08003550 .substreams = 2,
Harald Welted949cac2008-09-09 15:56:01 +08003551 .channels_min = 2,
3552 .channels_max = 2,
3553 .nid = 0x10, /* NID to query formats and rates */
3554 .ops = {
3555 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08003556 .prepare = via_playback_multi_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08003557 .cleanup = via_playback_multi_pcm_cleanup,
3558 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003559 },
3560};
3561
3562static struct hda_pcm_stream vt1702_pcm_analog_capture = {
3563 .substreams = 3,
3564 .channels_min = 2,
3565 .channels_max = 2,
3566 .nid = 0x12, /* NID to query formats and rates */
3567 .ops = {
Lydia Wang17314372009-10-10 19:07:37 +08003568 .open = via_pcm_open_close,
Harald Welted949cac2008-09-09 15:56:01 +08003569 .prepare = via_capture_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08003570 .cleanup = via_capture_pcm_cleanup,
3571 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003572 },
3573};
3574
3575static struct hda_pcm_stream vt1702_pcm_digital_playback = {
Harald Welte5691ec72008-09-15 22:42:26 +08003576 .substreams = 2,
Harald Welted949cac2008-09-09 15:56:01 +08003577 .channels_min = 2,
3578 .channels_max = 2,
3579 /* NID is set in via_build_pcms */
3580 .ops = {
3581 .open = via_dig_playback_pcm_open,
3582 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02003583 .prepare = via_dig_playback_pcm_prepare,
3584 .cleanup = via_dig_playback_pcm_cleanup
Harald Welted949cac2008-09-09 15:56:01 +08003585 },
3586};
3587
3588/* fill in the dac_nids table from the parsed pin configuration */
3589static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
3590 const struct auto_pin_cfg *cfg)
3591{
3592 spec->multiout.num_dacs = 1;
3593 spec->multiout.dac_nids = spec->private_dac_nids;
3594
3595 if (cfg->line_out_pins[0]) {
3596 /* config dac list */
3597 spec->multiout.dac_nids[0] = 0x10;
3598 }
3599
3600 return 0;
3601}
3602
3603/* add playback controls from the parsed DAC table */
3604static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
3605 const struct auto_pin_cfg *cfg)
3606{
3607 int err;
3608
3609 if (!cfg->line_out_pins[0])
3610 return -1;
3611
3612 /* add control to mixer index 0 */
3613 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3614 "Master Front Playback Volume",
3615 HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
3616 if (err < 0)
3617 return err;
3618 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3619 "Master Front Playback Switch",
3620 HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
3621 if (err < 0)
3622 return err;
3623
3624 /* Front */
3625 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3626 "Front Playback Volume",
3627 HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
3628 if (err < 0)
3629 return err;
3630 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3631 "Front Playback Switch",
3632 HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
3633 if (err < 0)
3634 return err;
3635
3636 return 0;
3637}
3638
3639static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3640{
Lydia Wang0713efe2009-10-10 19:07:43 +08003641 int err, i;
3642 struct hda_input_mux *imux;
3643 static const char *texts[] = { "ON", "OFF", NULL};
Harald Welted949cac2008-09-09 15:56:01 +08003644 if (!pin)
3645 return 0;
Harald Welted949cac2008-09-09 15:56:01 +08003646 spec->multiout.hp_nid = 0x1D;
Lydia Wangcdc17842009-10-10 19:07:47 +08003647 spec->hp_independent_mode_index = 0;
Harald Welted949cac2008-09-09 15:56:01 +08003648
3649 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3650 "Headphone Playback Volume",
3651 HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
3652 if (err < 0)
3653 return err;
3654
3655 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3656 "Headphone Playback Switch",
3657 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3658 if (err < 0)
3659 return err;
3660
Lydia Wang0713efe2009-10-10 19:07:43 +08003661 imux = &spec->private_imux[1];
Harald Welte0aa62ae2008-09-09 15:58:27 +08003662
Lydia Wang0713efe2009-10-10 19:07:43 +08003663 /* for hp mode select */
3664 i = 0;
3665 while (texts[i] != NULL) {
3666 imux->items[imux->num_items].label = texts[i];
3667 imux->items[imux->num_items].index = i;
3668 imux->num_items++;
3669 i++;
3670 }
3671
3672 spec->hp_mux = &spec->private_imux[1];
Harald Welted949cac2008-09-09 15:56:01 +08003673 return 0;
3674}
3675
3676/* create playback/capture controls for input pins */
3677static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
3678 const struct auto_pin_cfg *cfg)
3679{
3680 static char *labels[] = {
3681 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
3682 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08003683 struct hda_input_mux *imux = &spec->private_imux[0];
Harald Welted949cac2008-09-09 15:56:01 +08003684 int i, err, idx = 0;
3685
3686 /* for internal loopback recording select */
3687 imux->items[imux->num_items].label = "Stereo Mixer";
3688 imux->items[imux->num_items].index = 3;
3689 imux->num_items++;
3690
3691 for (i = 0; i < AUTO_PIN_LAST; i++) {
3692 if (!cfg->input_pins[i])
3693 continue;
3694
3695 switch (cfg->input_pins[i]) {
3696 case 0x14: /* Mic */
3697 idx = 1;
3698 break;
3699
3700 case 0x15: /* Line In */
3701 idx = 2;
3702 break;
3703
3704 case 0x18: /* Front Mic */
3705 idx = 3;
3706 break;
3707 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08003708 err = via_new_analog_input(spec, labels[i], idx, 0x1A);
Harald Welted949cac2008-09-09 15:56:01 +08003709 if (err < 0)
3710 return err;
3711 imux->items[imux->num_items].label = labels[i];
3712 imux->items[imux->num_items].index = idx-1;
3713 imux->num_items++;
3714 }
3715 return 0;
3716}
3717
3718static int vt1702_parse_auto_config(struct hda_codec *codec)
3719{
3720 struct via_spec *spec = codec->spec;
3721 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003722
Takashi Iwai9da29272009-05-07 16:31:14 +02003723 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
Harald Welted949cac2008-09-09 15:56:01 +08003724 if (err < 0)
3725 return err;
3726 err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
3727 if (err < 0)
3728 return err;
3729 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3730 return 0; /* can't find valid BIOS pin config */
3731
3732 err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
3733 if (err < 0)
3734 return err;
3735 err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3736 if (err < 0)
3737 return err;
Lydia Wangc2c02ea2009-10-10 19:07:32 +08003738 /* limit AA path volume to 0 dB */
3739 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3740 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3741 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3742 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3743 (1 << AC_AMPCAP_MUTE_SHIFT));
Harald Welted949cac2008-09-09 15:56:01 +08003744 err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
3745 if (err < 0)
3746 return err;
3747
3748 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3749
Takashi Iwai9da29272009-05-07 16:31:14 +02003750 fill_dig_outs(codec);
Harald Welte98aa34c2008-09-09 16:02:09 +08003751
Takashi Iwai603c4012008-07-30 15:01:44 +02003752 if (spec->kctls.list)
3753 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Harald Welted949cac2008-09-09 15:56:01 +08003754
Harald Welte0aa62ae2008-09-09 15:58:27 +08003755 spec->input_mux = &spec->private_imux[0];
3756
Harald Weltef8fdd492008-09-15 22:41:31 +08003757 if (spec->hp_mux)
3758 spec->mixers[spec->num_mixers++] = via_hp_mixer;
Harald Welted949cac2008-09-09 15:56:01 +08003759
3760 return 1;
3761}
3762
3763#ifdef CONFIG_SND_HDA_POWER_SAVE
3764static struct hda_amp_list vt1702_loopbacks[] = {
3765 { 0x1A, HDA_INPUT, 1 },
3766 { 0x1A, HDA_INPUT, 2 },
3767 { 0x1A, HDA_INPUT, 3 },
3768 { 0x1A, HDA_INPUT, 4 },
3769 { } /* end */
3770};
3771#endif
3772
3773static int patch_vt1702(struct hda_codec *codec)
3774{
3775 struct via_spec *spec;
3776 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003777
3778 /* create a codec specific record */
3779 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3780 if (spec == NULL)
3781 return -ENOMEM;
3782
3783 codec->spec = spec;
3784
3785 /* automatic parse from the BIOS config */
3786 err = vt1702_parse_auto_config(codec);
3787 if (err < 0) {
3788 via_free(codec);
3789 return err;
3790 } else if (!err) {
3791 printk(KERN_INFO "hda_codec: Cannot set up configuration "
3792 "from BIOS. Using genenic mode...\n");
3793 }
3794
Harald Welte69e52a82008-09-09 15:57:32 +08003795 spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
3796 spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003797
3798 spec->stream_name_analog = "VT1702 Analog";
3799 spec->stream_analog_playback = &vt1702_pcm_analog_playback;
3800 spec->stream_analog_capture = &vt1702_pcm_analog_capture;
3801
3802 spec->stream_name_digital = "VT1702 Digital";
3803 spec->stream_digital_playback = &vt1702_pcm_digital_playback;
3804
3805 if (!spec->adc_nids && spec->input_mux) {
3806 spec->adc_nids = vt1702_adc_nids;
3807 spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003808 get_mux_nids(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003809 spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
3810 spec->num_mixers++;
3811 }
3812
3813 codec->patch_ops = via_patch_ops;
3814
3815 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003816 codec->patch_ops.unsol_event = via_unsol_event;
Harald Welted949cac2008-09-09 15:56:01 +08003817#ifdef CONFIG_SND_HDA_POWER_SAVE
3818 spec->loopback.amplist = vt1702_loopbacks;
3819#endif
3820
Harald Welted949cac2008-09-09 15:56:01 +08003821 return 0;
3822}
3823
Joseph Chanc577b8a2006-11-29 15:29:40 +01003824/*
3825 * patch entries
3826 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003827static struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003828 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3829 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3830 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3831 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3832 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003833 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003834 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003835 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003836 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003837 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003838 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003839 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003840 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003841 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003842 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003843 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003844 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003845 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003846 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003847 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003848 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003849 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003850 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003851 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003852 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003853 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003854 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003855 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003856 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003857 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003858 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003859 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003860 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003861 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003862 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003863 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003864 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003865 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003866 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003867 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003868 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003869 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003870 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003871 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003872 { .id = 0x11064397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003873 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003874 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003875 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003876 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003877 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003878 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003879 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003880 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003881 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003882 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003883 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003884 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003885 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003886 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003887 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003888 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003889 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003890 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003891 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003892 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003893 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003894 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003895 .patch = patch_vt1702},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003896 {} /* terminator */
3897};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003898
3899MODULE_ALIAS("snd-hda-codec-id:1106*");
3900
3901static struct hda_codec_preset_list via_list = {
3902 .preset = snd_hda_preset_via,
3903 .owner = THIS_MODULE,
3904};
3905
3906MODULE_LICENSE("GPL");
3907MODULE_DESCRIPTION("VIA HD-audio codec");
3908
3909static int __init patch_via_init(void)
3910{
3911 return snd_hda_add_codec_preset(&via_list);
3912}
3913
3914static void __exit patch_via_exit(void)
3915{
3916 snd_hda_delete_codec_preset(&via_list);
3917}
3918
3919module_init(patch_via_init)
3920module_exit(patch_via_exit)