blob: 2e7e72c83a5205a465e34c8821eb6b0fe64b499d [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,
Lydia Wangeb7188c2009-10-10 19:08:34 +080089 VT1718S,
Harald Welted7426322008-09-15 22:43:23 +080090 CODEC_TYPES,
91};
92
Lydia Wang1f2e99f2009-10-10 19:08:17 +080093struct via_spec {
94 /* codec parameterization */
95 struct snd_kcontrol_new *mixers[4];
96 unsigned int num_mixers;
97
98 struct hda_verb *init_verbs[5];
99 unsigned int num_iverbs;
100
101 char *stream_name_analog;
102 struct hda_pcm_stream *stream_analog_playback;
103 struct hda_pcm_stream *stream_analog_capture;
104
105 char *stream_name_digital;
106 struct hda_pcm_stream *stream_digital_playback;
107 struct hda_pcm_stream *stream_digital_capture;
108
109 /* playback */
110 struct hda_multi_out multiout;
111 hda_nid_t slave_dig_outs[2];
112
113 /* capture */
114 unsigned int num_adc_nids;
115 hda_nid_t *adc_nids;
116 hda_nid_t mux_nids[3];
117 hda_nid_t dig_in_nid;
118 hda_nid_t dig_in_pin;
119
120 /* capture source */
121 const struct hda_input_mux *input_mux;
122 unsigned int cur_mux[3];
123
124 /* PCM information */
125 struct hda_pcm pcm_rec[3];
126
127 /* dynamic controls, init_verbs and input_mux */
128 struct auto_pin_cfg autocfg;
129 struct snd_array kctls;
130 struct hda_input_mux private_imux[2];
131 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
132
133 /* HP mode source */
134 const struct hda_input_mux *hp_mux;
135 unsigned int hp_independent_mode;
136 unsigned int hp_independent_mode_index;
137 unsigned int smart51_enabled;
138
139 enum VIA_HDA_CODEC codec_type;
140
141 /* work to check hp jack state */
142 struct hda_codec *codec;
143 struct delayed_work vt1708_hp_work;
144 int vt1708_jack_detectect;
145 int vt1708_hp_present;
146#ifdef CONFIG_SND_HDA_POWER_SAVE
147 struct hda_loopback_check loopback;
148#endif
149};
150
Lydia Wang744ff5f2009-10-10 19:07:26 +0800151static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800152{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800153 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800154 u16 ven_id = vendor_id >> 16;
155 u16 dev_id = vendor_id & 0xffff;
156 enum VIA_HDA_CODEC codec_type;
157
158 /* get codec type */
159 if (ven_id != 0x1106)
160 codec_type = UNKNOWN;
161 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
162 codec_type = VT1708;
163 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
164 codec_type = VT1709_10CH;
165 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
166 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800167 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800168 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800169 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
170 codec_type = VT1708BCE;
171 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800172 codec_type = VT1708B_4CH;
173 else if ((dev_id & 0xfff) == 0x397
174 && (dev_id >> 12) < 8)
175 codec_type = VT1708S;
176 else if ((dev_id & 0xfff) == 0x398
177 && (dev_id >> 12) < 8)
178 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800179 else if ((dev_id & 0xfff) == 0x428
180 && (dev_id >> 12) < 8)
181 codec_type = VT1718S;
Lydia Wangbb3c6bf2009-10-10 19:08:39 +0800182 else if (dev_id == 0x0441 || dev_id == 0x4441)
183 codec_type = VT1718S;
Harald Welted7426322008-09-15 22:43:23 +0800184 else
185 codec_type = UNKNOWN;
186 return codec_type;
187};
188
Harald Welte69e52a82008-09-09 15:57:32 +0800189#define VIA_HP_EVENT 0x01
190#define VIA_GPIO_EVENT 0x02
Lydia Wanga34df192009-10-10 19:08:01 +0800191#define VIA_JACK_EVENT 0x04
Harald Welte69e52a82008-09-09 15:57:32 +0800192
Joseph Chanc577b8a2006-11-29 15:29:40 +0100193enum {
194 VIA_CTL_WIDGET_VOL,
195 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800196 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100197};
198
199enum {
Harald Welteeb14a462008-09-09 15:40:38 +0800200 AUTO_SEQ_FRONT = 0,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100201 AUTO_SEQ_SURROUND,
202 AUTO_SEQ_CENLFE,
203 AUTO_SEQ_SIDE
204};
205
Lydia Wangf5271102009-10-10 19:07:35 +0800206static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
207static void set_jack_power_state(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800208static int is_aa_path_mute(struct hda_codec *codec);
209
210static void vt1708_start_hp_work(struct via_spec *spec)
211{
212 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
213 return;
214 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
215 !spec->vt1708_jack_detectect);
216 if (!delayed_work_pending(&spec->vt1708_hp_work))
217 schedule_delayed_work(&spec->vt1708_hp_work,
218 msecs_to_jiffies(100));
219}
220
221static void vt1708_stop_hp_work(struct via_spec *spec)
222{
223 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
224 return;
225 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
226 && !is_aa_path_mute(spec->codec))
227 return;
228 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
229 !spec->vt1708_jack_detectect);
230 cancel_delayed_work(&spec->vt1708_hp_work);
231 flush_scheduled_work();
232}
Lydia Wangf5271102009-10-10 19:07:35 +0800233
234static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
235 struct snd_ctl_elem_value *ucontrol)
236{
237 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
238 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
239
240 set_jack_power_state(codec);
241 analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800242 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
243 if (is_aa_path_mute(codec))
244 vt1708_start_hp_work(codec->spec);
245 else
246 vt1708_stop_hp_work(codec->spec);
247 }
Lydia Wangf5271102009-10-10 19:07:35 +0800248 return change;
249}
250
251/* modify .put = snd_hda_mixer_amp_switch_put */
252#define ANALOG_INPUT_MUTE \
253 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
254 .name = NULL, \
255 .index = 0, \
256 .info = snd_hda_mixer_amp_switch_info, \
257 .get = snd_hda_mixer_amp_switch_get, \
258 .put = analog_input_switch_put, \
259 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
260
Joseph Chanc577b8a2006-11-29 15:29:40 +0100261static struct snd_kcontrol_new vt1708_control_templates[] = {
262 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
263 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800264 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100265};
266
267
Joseph Chanc577b8a2006-11-29 15:29:40 +0100268static hda_nid_t vt1708_adc_nids[2] = {
269 /* ADC1-2 */
270 0x15, 0x27
271};
272
273static hda_nid_t vt1709_adc_nids[3] = {
274 /* ADC1-2 */
275 0x14, 0x15, 0x16
276};
277
Josepch Chanf7278fd2007-12-13 16:40:40 +0100278static hda_nid_t vt1708B_adc_nids[2] = {
279 /* ADC1-2 */
280 0x13, 0x14
281};
282
Harald Welted949cac2008-09-09 15:56:01 +0800283static hda_nid_t vt1708S_adc_nids[2] = {
284 /* ADC1-2 */
285 0x13, 0x14
286};
287
288static hda_nid_t vt1702_adc_nids[3] = {
289 /* ADC1-2 */
290 0x12, 0x20, 0x1F
291};
292
Lydia Wangeb7188c2009-10-10 19:08:34 +0800293static hda_nid_t vt1718S_adc_nids[2] = {
294 /* ADC1-2 */
295 0x10, 0x11
296};
297
Joseph Chanc577b8a2006-11-29 15:29:40 +0100298/* add dynamic controls */
299static int via_add_control(struct via_spec *spec, int type, const char *name,
300 unsigned long val)
301{
302 struct snd_kcontrol_new *knew;
303
Takashi Iwai603c4012008-07-30 15:01:44 +0200304 snd_array_init(&spec->kctls, sizeof(*knew), 32);
305 knew = snd_array_new(&spec->kctls);
306 if (!knew)
307 return -ENOMEM;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100308 *knew = vt1708_control_templates[type];
309 knew->name = kstrdup(name, GFP_KERNEL);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100310 if (!knew->name)
311 return -ENOMEM;
312 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100313 return 0;
314}
315
Takashi Iwai603c4012008-07-30 15:01:44 +0200316static void via_free_kctls(struct hda_codec *codec)
317{
318 struct via_spec *spec = codec->spec;
319
320 if (spec->kctls.list) {
321 struct snd_kcontrol_new *kctl = spec->kctls.list;
322 int i;
323 for (i = 0; i < spec->kctls.used; i++)
324 kfree(kctl[i].name);
325 }
326 snd_array_free(&spec->kctls);
327}
328
Joseph Chanc577b8a2006-11-29 15:29:40 +0100329/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800330static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
331 int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100332{
333 char name[32];
334 int err;
335
336 sprintf(name, "%s Playback Volume", ctlname);
337 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
338 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
339 if (err < 0)
340 return err;
341 sprintf(name, "%s Playback Switch", ctlname);
Lydia Wangf5271102009-10-10 19:07:35 +0800342 err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100343 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
344 if (err < 0)
345 return err;
346 return 0;
347}
348
349static void via_auto_set_output_and_unmute(struct hda_codec *codec,
350 hda_nid_t nid, int pin_type,
351 int dac_idx)
352{
353 /* set as output */
354 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
355 pin_type);
356 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
357 AMP_OUT_UNMUTE);
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200358 if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
359 snd_hda_codec_write(codec, nid, 0,
360 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100361}
362
363
364static void via_auto_init_multi_out(struct hda_codec *codec)
365{
366 struct via_spec *spec = codec->spec;
367 int i;
368
369 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
370 hda_nid_t nid = spec->autocfg.line_out_pins[i];
371 if (nid)
372 via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
373 }
374}
375
376static void via_auto_init_hp_out(struct hda_codec *codec)
377{
378 struct via_spec *spec = codec->spec;
379 hda_nid_t pin;
380
381 pin = spec->autocfg.hp_pins[0];
382 if (pin) /* connect to front */
383 via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
384}
385
386static void via_auto_init_analog_input(struct hda_codec *codec)
387{
388 struct via_spec *spec = codec->spec;
389 int i;
390
391 for (i = 0; i < AUTO_PIN_LAST; i++) {
392 hda_nid_t nid = spec->autocfg.input_pins[i];
393
394 snd_hda_codec_write(codec, nid, 0,
395 AC_VERB_SET_PIN_WIDGET_CONTROL,
396 (i <= AUTO_PIN_FRONT_MIC ?
397 PIN_VREF50 : PIN_IN));
398
399 }
400}
Lydia Wangf5271102009-10-10 19:07:35 +0800401
Lydia Wang1564b282009-10-10 19:07:52 +0800402static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
403
Lydia Wangf5271102009-10-10 19:07:35 +0800404static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
405 unsigned int *affected_parm)
406{
407 unsigned parm;
408 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
409 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
410 >> AC_DEFCFG_MISC_SHIFT
411 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
412 unsigned present = snd_hda_codec_read(codec, nid, 0,
413 AC_VERB_GET_PIN_SENSE, 0) >> 31;
Lydia Wang1564b282009-10-10 19:07:52 +0800414 struct via_spec *spec = codec->spec;
415 if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
416 || ((no_presence || present)
417 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800418 *affected_parm = AC_PWRST_D0; /* if it's connected */
419 parm = AC_PWRST_D0;
420 } else
421 parm = AC_PWRST_D3;
422
423 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
424}
425
426static void set_jack_power_state(struct hda_codec *codec)
427{
428 struct via_spec *spec = codec->spec;
429 int imux_is_smixer;
430 unsigned int parm;
431
432 if (spec->codec_type == VT1702) {
433 imux_is_smixer = snd_hda_codec_read(
434 codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
435 /* inputs */
436 /* PW 1/2/5 (14h/15h/18h) */
437 parm = AC_PWRST_D3;
438 set_pin_power_state(codec, 0x14, &parm);
439 set_pin_power_state(codec, 0x15, &parm);
440 set_pin_power_state(codec, 0x18, &parm);
441 if (imux_is_smixer)
442 parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */
443 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
444 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
445 parm);
446 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE,
447 parm);
448 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
449 parm);
450 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE,
451 parm);
452
453 /* outputs */
454 /* PW 3/4 (16h/17h) */
455 parm = AC_PWRST_D3;
456 set_pin_power_state(codec, 0x16, &parm);
457 set_pin_power_state(codec, 0x17, &parm);
458 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
459 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
460 imux_is_smixer ? AC_PWRST_D0 : parm);
461 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
462 parm);
463 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE,
464 parm);
465 } else if (spec->codec_type == VT1708B_8CH
466 || spec->codec_type == VT1708B_4CH
467 || spec->codec_type == VT1708S) {
468 /* SW0 (17h) = stereo mixer */
469 int is_8ch = spec->codec_type != VT1708B_4CH;
470 imux_is_smixer = snd_hda_codec_read(
471 codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
472 == ((spec->codec_type == VT1708S) ? 5 : 0);
473 /* inputs */
474 /* PW 1/2/5 (1ah/1bh/1eh) */
475 parm = AC_PWRST_D3;
476 set_pin_power_state(codec, 0x1a, &parm);
477 set_pin_power_state(codec, 0x1b, &parm);
478 set_pin_power_state(codec, 0x1e, &parm);
479 if (imux_is_smixer)
480 parm = AC_PWRST_D0;
481 /* SW0 (17h), AIW 0/1 (13h/14h) */
482 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
483 parm);
484 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
485 parm);
486 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
487 parm);
488
489 /* outputs */
490 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
491 parm = AC_PWRST_D3;
492 set_pin_power_state(codec, 0x19, &parm);
493 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
494 parm);
495 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
496 parm);
497
498 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
499 if (is_8ch) {
500 parm = AC_PWRST_D3;
501 set_pin_power_state(codec, 0x22, &parm);
502 snd_hda_codec_write(codec, 0x26, 0,
503 AC_VERB_SET_POWER_STATE, parm);
504 snd_hda_codec_write(codec, 0x24, 0,
505 AC_VERB_SET_POWER_STATE, parm);
506 }
507
508 /* PW 3/4/7 (1ch/1dh/23h) */
509 parm = AC_PWRST_D3;
510 /* force to D0 for internal Speaker */
511 set_pin_power_state(codec, 0x1c, &parm);
512 set_pin_power_state(codec, 0x1d, &parm);
513 if (is_8ch)
514 set_pin_power_state(codec, 0x23, &parm);
515 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
516 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
517 imux_is_smixer ? AC_PWRST_D0 : parm);
518 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
519 parm);
520 if (is_8ch) {
521 snd_hda_codec_write(codec, 0x25, 0,
522 AC_VERB_SET_POWER_STATE, parm);
523 snd_hda_codec_write(codec, 0x27, 0,
524 AC_VERB_SET_POWER_STATE, parm);
525 }
Lydia Wangeb7188c2009-10-10 19:08:34 +0800526 } else if (spec->codec_type == VT1718S) {
527 /* MUX6 (1eh) = stereo mixer */
528 imux_is_smixer = snd_hda_codec_read(
529 codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
530 /* inputs */
531 /* PW 5/6/7 (29h/2ah/2bh) */
532 parm = AC_PWRST_D3;
533 set_pin_power_state(codec, 0x29, &parm);
534 set_pin_power_state(codec, 0x2a, &parm);
535 set_pin_power_state(codec, 0x2b, &parm);
536 if (imux_is_smixer)
537 parm = AC_PWRST_D0;
538 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
539 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE,
540 parm);
541 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
542 parm);
543 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
544 parm);
545 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
546 parm);
547
548 /* outputs */
549 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
550 parm = AC_PWRST_D3;
551 set_pin_power_state(codec, 0x27, &parm);
552 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
553 parm);
554 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE,
555 parm);
556
557 /* PW2 (26h), AOW2 (ah) */
558 parm = AC_PWRST_D3;
559 set_pin_power_state(codec, 0x26, &parm);
560 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE,
561 parm);
562
563 /* PW0/1 (24h/25h) */
564 parm = AC_PWRST_D3;
565 set_pin_power_state(codec, 0x24, &parm);
566 set_pin_power_state(codec, 0x25, &parm);
567 if (!spec->hp_independent_mode) /* check for redirected HP */
568 set_pin_power_state(codec, 0x28, &parm);
569 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE,
570 parm);
571 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE,
572 parm);
573 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
574 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
575 imux_is_smixer ? AC_PWRST_D0 : parm);
576 if (spec->hp_independent_mode) {
577 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
578 parm = AC_PWRST_D3;
579 set_pin_power_state(codec, 0x28, &parm);
580 snd_hda_codec_write(codec, 0x1b, 0,
581 AC_VERB_SET_POWER_STATE, parm);
582 snd_hda_codec_write(codec, 0x34, 0,
583 AC_VERB_SET_POWER_STATE, parm);
584 snd_hda_codec_write(codec, 0xc, 0,
585 AC_VERB_SET_POWER_STATE, parm);
586 }
Lydia Wangf5271102009-10-10 19:07:35 +0800587 }
588}
589
Joseph Chanc577b8a2006-11-29 15:29:40 +0100590/*
591 * input MUX handling
592 */
593static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
594 struct snd_ctl_elem_info *uinfo)
595{
596 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
597 struct via_spec *spec = codec->spec;
598 return snd_hda_input_mux_info(spec->input_mux, uinfo);
599}
600
601static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
602 struct snd_ctl_elem_value *ucontrol)
603{
604 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
605 struct via_spec *spec = codec->spec;
606 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
607
608 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
609 return 0;
610}
611
612static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
613 struct snd_ctl_elem_value *ucontrol)
614{
615 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
616 struct via_spec *spec = codec->spec;
617 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100618
Takashi Iwai337b9d02009-07-07 18:18:59 +0200619 if (!spec->mux_nids[adc_idx])
620 return -EINVAL;
Lydia Wanga80e6e32009-10-10 19:07:55 +0800621 /* switch to D0 beofre change index */
622 if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
623 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
624 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
625 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
626 /* update jack power state */
627 set_jack_power_state(codec);
628
Takashi Iwai337b9d02009-07-07 18:18:59 +0200629 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
630 spec->mux_nids[adc_idx],
631 &spec->cur_mux[adc_idx]);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100632}
633
Harald Welte0aa62ae2008-09-09 15:58:27 +0800634static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
635 struct snd_ctl_elem_info *uinfo)
636{
637 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
638 struct via_spec *spec = codec->spec;
639 return snd_hda_input_mux_info(spec->hp_mux, uinfo);
640}
641
642static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
643 struct snd_ctl_elem_value *ucontrol)
644{
645 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
646 struct via_spec *spec = codec->spec;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800647 hda_nid_t nid;
648 unsigned int pinsel;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800649
Lydia Wangeb7188c2009-10-10 19:08:34 +0800650 switch (spec->codec_type) {
651 case VT1718S:
652 nid = 0x34;
653 break;
654 default:
655 nid = spec->autocfg.hp_pins[0];
656 break;
657 }
658 /* use !! to translate conn sel 2 for VT1718S */
659 pinsel = !!snd_hda_codec_read(codec, nid, 0,
660 AC_VERB_GET_CONNECT_SEL,
661 0x00);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800662 ucontrol->value.enumerated.item[0] = pinsel;
663
664 return 0;
665}
666
Lydia Wang0713efe2009-10-10 19:07:43 +0800667static void activate_ctl(struct hda_codec *codec, const char *name, int active)
668{
669 struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
670 if (ctl) {
671 ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
672 ctl->vd[0].access |= active
673 ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
674 snd_ctl_notify(codec->bus->card,
675 SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
676 }
677}
678
Lydia Wangcdc17842009-10-10 19:07:47 +0800679static int update_side_mute_status(struct hda_codec *codec)
680{
681 /* mute side channel */
682 struct via_spec *spec = codec->spec;
683 unsigned int parm = spec->hp_independent_mode
684 ? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
685 hda_nid_t sw3;
686
687 switch (spec->codec_type) {
688 case VT1708:
689 sw3 = 0x1b;
690 break;
691 case VT1709_10CH:
692 sw3 = 0x29;
693 break;
694 case VT1708B_8CH:
695 case VT1708S:
696 sw3 = 0x27;
697 break;
698 default:
699 sw3 = 0;
700 break;
701 }
702
703 if (sw3)
704 snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
705 parm);
706 return 0;
707}
708
Harald Welte0aa62ae2008-09-09 15:58:27 +0800709static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
710 struct snd_ctl_elem_value *ucontrol)
711{
712 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
713 struct via_spec *spec = codec->spec;
714 hda_nid_t nid = spec->autocfg.hp_pins[0];
715 unsigned int pinsel = ucontrol->value.enumerated.item[0];
Lydia Wangcdc17842009-10-10 19:07:47 +0800716 /* Get Independent Mode index of headphone pin widget */
717 spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
718 ? 1 : 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800719
Lydia Wangeb7188c2009-10-10 19:08:34 +0800720 switch (spec->codec_type) {
721 case VT1718S:
722 nid = 0x34;
723 pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */
724 spec->multiout.num_dacs = 4;
725 break;
726 default:
727 nid = spec->autocfg.hp_pins[0];
728 break;
729 }
Lydia Wangcdc17842009-10-10 19:07:47 +0800730 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800731
Lydia Wangcdc17842009-10-10 19:07:47 +0800732 if (spec->multiout.hp_nid && spec->multiout.hp_nid
733 != spec->multiout.dac_nids[HDA_FRONT])
734 snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
735 0, 0, 0);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800736
Lydia Wangcdc17842009-10-10 19:07:47 +0800737 update_side_mute_status(codec);
Lydia Wang0713efe2009-10-10 19:07:43 +0800738 /* update HP volume/swtich active state */
739 if (spec->codec_type == VT1708S
Lydia Wangeb7188c2009-10-10 19:08:34 +0800740 || spec->codec_type == VT1702
741 || spec->codec_type == VT1718S) {
Lydia Wang0713efe2009-10-10 19:07:43 +0800742 activate_ctl(codec, "Headphone Playback Volume",
743 spec->hp_independent_mode);
744 activate_ctl(codec, "Headphone Playback Switch",
745 spec->hp_independent_mode);
746 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800747 return 0;
748}
749
750static struct snd_kcontrol_new via_hp_mixer[] = {
751 {
752 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
753 .name = "Independent HP",
754 .count = 1,
755 .info = via_independent_hp_info,
756 .get = via_independent_hp_get,
757 .put = via_independent_hp_put,
758 },
759 { } /* end */
760};
761
Lydia Wang1564b282009-10-10 19:07:52 +0800762static void notify_aa_path_ctls(struct hda_codec *codec)
763{
764 int i;
765 struct snd_ctl_elem_id id;
766 const char *labels[] = {"Mic", "Front Mic", "Line"};
767
768 memset(&id, 0, sizeof(id));
769 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
770 for (i = 0; i < ARRAY_SIZE(labels); i++) {
771 sprintf(id.name, "%s Playback Volume", labels[i]);
772 snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
773 &id);
774 }
775}
776
777static void mute_aa_path(struct hda_codec *codec, int mute)
778{
779 struct via_spec *spec = codec->spec;
780 hda_nid_t nid_mixer;
781 int start_idx;
782 int end_idx;
783 int i;
784 /* get nid of MW0 and start & end index */
785 switch (spec->codec_type) {
786 case VT1708:
787 nid_mixer = 0x17;
788 start_idx = 2;
789 end_idx = 4;
790 break;
791 case VT1709_10CH:
792 case VT1709_6CH:
793 nid_mixer = 0x18;
794 start_idx = 2;
795 end_idx = 4;
796 break;
797 case VT1708B_8CH:
798 case VT1708B_4CH:
799 case VT1708S:
800 nid_mixer = 0x16;
801 start_idx = 2;
802 end_idx = 4;
803 break;
804 default:
805 return;
806 }
807 /* check AA path's mute status */
808 for (i = start_idx; i <= end_idx; i++) {
809 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
810 snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
811 HDA_AMP_MUTE, val);
812 }
813}
814static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
815{
816 int res = 0;
817 int index;
818 for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
819 if (pin == spec->autocfg.input_pins[index]) {
820 res = 1;
821 break;
822 }
823 }
824 return res;
825}
826
827static int via_smart51_info(struct snd_kcontrol *kcontrol,
828 struct snd_ctl_elem_info *uinfo)
829{
830 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
831 uinfo->count = 1;
832 uinfo->value.integer.min = 0;
833 uinfo->value.integer.max = 1;
834 return 0;
835}
836
837static int via_smart51_get(struct snd_kcontrol *kcontrol,
838 struct snd_ctl_elem_value *ucontrol)
839{
840 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
841 struct via_spec *spec = codec->spec;
842 int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
843 int on = 1;
844 int i;
845
846 for (i = 0; i < ARRAY_SIZE(index); i++) {
847 hda_nid_t nid = spec->autocfg.input_pins[index[i]];
848 if (nid) {
849 int ctl =
850 snd_hda_codec_read(codec, nid, 0,
851 AC_VERB_GET_PIN_WIDGET_CONTROL,
852 0);
853 if (i == AUTO_PIN_FRONT_MIC
Lydia Wangeb7188c2009-10-10 19:08:34 +0800854 && spec->hp_independent_mode
855 && spec->codec_type != VT1718S)
Lydia Wang1564b282009-10-10 19:07:52 +0800856 continue; /* ignore FMic for independent HP */
857 if (ctl & AC_PINCTL_IN_EN
858 && !(ctl & AC_PINCTL_OUT_EN))
859 on = 0;
860 }
861 }
862 *ucontrol->value.integer.value = on;
863 return 0;
864}
865
866static int via_smart51_put(struct snd_kcontrol *kcontrol,
867 struct snd_ctl_elem_value *ucontrol)
868{
869 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
870 struct via_spec *spec = codec->spec;
871 int out_in = *ucontrol->value.integer.value
872 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
873 int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
874 int i;
875
876 for (i = 0; i < ARRAY_SIZE(index); i++) {
877 hda_nid_t nid = spec->autocfg.input_pins[index[i]];
878 if (i == AUTO_PIN_FRONT_MIC
Lydia Wangeb7188c2009-10-10 19:08:34 +0800879 && spec->hp_independent_mode
880 && spec->codec_type != VT1718S)
Lydia Wang1564b282009-10-10 19:07:52 +0800881 continue; /* don't retask FMic for independent HP */
882 if (nid) {
883 unsigned int parm = snd_hda_codec_read(
884 codec, nid, 0,
885 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
886 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
887 parm |= out_in;
888 snd_hda_codec_write(codec, nid, 0,
889 AC_VERB_SET_PIN_WIDGET_CONTROL,
890 parm);
891 if (out_in == AC_PINCTL_OUT_EN) {
892 mute_aa_path(codec, 1);
893 notify_aa_path_ctls(codec);
894 }
Lydia Wangeb7188c2009-10-10 19:08:34 +0800895 if (spec->codec_type == VT1718S)
896 snd_hda_codec_amp_stereo(
897 codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
898 HDA_AMP_UNMUTE);
Lydia Wang1564b282009-10-10 19:07:52 +0800899 }
900 if (i == AUTO_PIN_FRONT_MIC) {
901 if (spec->codec_type == VT1708S) {
902 /* input = index 1 (AOW3) */
903 snd_hda_codec_write(
904 codec, nid, 0,
905 AC_VERB_SET_CONNECT_SEL, 1);
906 snd_hda_codec_amp_stereo(
907 codec, nid, HDA_OUTPUT,
908 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
909 }
910 }
911 }
912 spec->smart51_enabled = *ucontrol->value.integer.value;
913 set_jack_power_state(codec);
914 return 1;
915}
916
917static struct snd_kcontrol_new via_smart51_mixer[] = {
918 {
919 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
920 .name = "Smart 5.1",
921 .count = 1,
922 .info = via_smart51_info,
923 .get = via_smart51_get,
924 .put = via_smart51_put,
925 },
926 {} /* end */
927};
928
Joseph Chanc577b8a2006-11-29 15:29:40 +0100929/* capture mixer elements */
930static struct snd_kcontrol_new vt1708_capture_mixer[] = {
931 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
932 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
933 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
934 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
935 {
936 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
937 /* The multiple "Capture Source" controls confuse alsamixer
938 * So call somewhat different..
Joseph Chanc577b8a2006-11-29 15:29:40 +0100939 */
940 /* .name = "Capture Source", */
941 .name = "Input Source",
942 .count = 1,
943 .info = via_mux_enum_info,
944 .get = via_mux_enum_get,
945 .put = via_mux_enum_put,
946 },
947 { } /* end */
948};
Lydia Wangf5271102009-10-10 19:07:35 +0800949
950/* check AA path's mute statue */
951static int is_aa_path_mute(struct hda_codec *codec)
952{
953 int mute = 1;
954 hda_nid_t nid_mixer;
955 int start_idx;
956 int end_idx;
957 int i;
958 struct via_spec *spec = codec->spec;
959 /* get nid of MW0 and start & end index */
960 switch (spec->codec_type) {
961 case VT1708B_8CH:
962 case VT1708B_4CH:
963 case VT1708S:
964 nid_mixer = 0x16;
965 start_idx = 2;
966 end_idx = 4;
967 break;
968 case VT1702:
969 nid_mixer = 0x1a;
970 start_idx = 1;
971 end_idx = 3;
972 break;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800973 case VT1718S:
974 nid_mixer = 0x21;
975 start_idx = 1;
976 end_idx = 3;
977 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800978 default:
979 return 0;
980 }
981 /* check AA path's mute status */
982 for (i = start_idx; i <= end_idx; i++) {
983 unsigned int con_list = snd_hda_codec_read(
984 codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
985 int shift = 8 * (i % 4);
986 hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
987 unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
988 if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
989 /* check mute status while the pin is connected */
990 int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
991 HDA_INPUT, i) >> 7;
992 int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
993 HDA_INPUT, i) >> 7;
994 if (!mute_l || !mute_r) {
995 mute = 0;
996 break;
997 }
998 }
999 }
1000 return mute;
1001}
1002
1003/* enter/exit analog low-current mode */
1004static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
1005{
1006 struct via_spec *spec = codec->spec;
1007 static int saved_stream_idle = 1; /* saved stream idle status */
1008 int enable = is_aa_path_mute(codec);
1009 unsigned int verb = 0;
1010 unsigned int parm = 0;
1011
1012 if (stream_idle == -1) /* stream status did not change */
1013 enable = enable && saved_stream_idle;
1014 else {
1015 enable = enable && stream_idle;
1016 saved_stream_idle = stream_idle;
1017 }
1018
1019 /* decide low current mode's verb & parameter */
1020 switch (spec->codec_type) {
1021 case VT1708B_8CH:
1022 case VT1708B_4CH:
1023 verb = 0xf70;
1024 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1025 break;
1026 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001027 case VT1718S:
Lydia Wangf5271102009-10-10 19:07:35 +08001028 verb = 0xf73;
1029 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1030 break;
1031 case VT1702:
1032 verb = 0xf73;
1033 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1034 break;
1035 default:
1036 return; /* other codecs are not supported */
1037 }
1038 /* send verb */
1039 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1040}
1041
Joseph Chanc577b8a2006-11-29 15:29:40 +01001042/*
1043 * generic initialization of ADC, input mixers and output mixers
1044 */
1045static struct hda_verb vt1708_volume_init_verbs[] = {
1046 /*
1047 * Unmute ADC0-1 and set the default input to mic-in
1048 */
1049 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1050 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1051
1052
Josepch Chanf7278fd2007-12-13 16:40:40 +01001053 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
Joseph Chanc577b8a2006-11-29 15:29:40 +01001054 * mixer widget
1055 */
1056 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
Josepch Chanf7278fd2007-12-13 16:40:40 +01001057 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1058 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1059 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
1060 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
1061 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
Joseph Chanc577b8a2006-11-29 15:29:40 +01001062
1063 /*
1064 * Set up output mixers (0x19 - 0x1b)
1065 */
1066 /* set vol=0 to output mixers */
1067 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1068 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1069 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1070
1071 /* Setup default input to PW4 */
1072 {0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
Joseph Chanc577b8a2006-11-29 15:29:40 +01001073 /* PW9 Output enable */
1074 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001075 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001076};
1077
1078static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
1079 struct hda_codec *codec,
1080 struct snd_pcm_substream *substream)
1081{
1082 struct via_spec *spec = codec->spec;
Lydia Wang17314372009-10-10 19:07:37 +08001083 int idle = substream->pstr->substream_opened == 1
1084 && substream->ref_count == 0;
Lydia Wang17314372009-10-10 19:07:37 +08001085 analog_low_current_mode(codec, idle);
Takashi Iwai9a081602008-02-12 18:37:26 +01001086 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1087 hinfo);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001088}
1089
Harald Welte0aa62ae2008-09-09 15:58:27 +08001090static void playback_multi_pcm_prep_0(struct hda_codec *codec,
1091 unsigned int stream_tag,
1092 unsigned int format,
1093 struct snd_pcm_substream *substream)
1094{
1095 struct via_spec *spec = codec->spec;
1096 struct hda_multi_out *mout = &spec->multiout;
1097 hda_nid_t *nids = mout->dac_nids;
1098 int chs = substream->runtime->channels;
1099 int i;
1100
1101 mutex_lock(&codec->spdif_mutex);
1102 if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
1103 if (chs == 2 &&
1104 snd_hda_is_supported_format(codec, mout->dig_out_nid,
1105 format) &&
1106 !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
1107 mout->dig_out_used = HDA_DIG_ANALOG_DUP;
1108 /* turn off SPDIF once; otherwise the IEC958 bits won't
1109 * be updated */
1110 if (codec->spdif_ctls & AC_DIG1_ENABLE)
1111 snd_hda_codec_write(codec, mout->dig_out_nid, 0,
1112 AC_VERB_SET_DIGI_CONVERT_1,
1113 codec->spdif_ctls &
1114 ~AC_DIG1_ENABLE & 0xff);
1115 snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
1116 stream_tag, 0, format);
1117 /* turn on again (if needed) */
1118 if (codec->spdif_ctls & AC_DIG1_ENABLE)
1119 snd_hda_codec_write(codec, mout->dig_out_nid, 0,
1120 AC_VERB_SET_DIGI_CONVERT_1,
1121 codec->spdif_ctls & 0xff);
1122 } else {
1123 mout->dig_out_used = 0;
1124 snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
1125 0, 0, 0);
1126 }
1127 }
1128 mutex_unlock(&codec->spdif_mutex);
1129
1130 /* front */
1131 snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
1132 0, format);
1133
Lydia Wangeb7188c2009-10-10 19:08:34 +08001134 if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
1135 && !spec->hp_independent_mode)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001136 /* headphone out will just decode front left/right (stereo) */
1137 snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
1138 0, format);
1139
1140 /* extra outputs copied from front */
1141 for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
1142 if (mout->extra_out_nid[i])
1143 snd_hda_codec_setup_stream(codec,
1144 mout->extra_out_nid[i],
1145 stream_tag, 0, format);
1146
1147 /* surrounds */
1148 for (i = 1; i < mout->num_dacs; i++) {
1149 if (chs >= (i + 1) * 2) /* independent out */
1150 snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
1151 i * 2, format);
1152 else /* copy front */
1153 snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
1154 0, format);
1155 }
1156}
1157
1158static int via_playback_multi_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 struct hda_multi_out *mout = &spec->multiout;
1166 hda_nid_t *nids = mout->dac_nids;
1167
1168 if (substream->number == 0)
1169 playback_multi_pcm_prep_0(codec, stream_tag, format,
1170 substream);
1171 else {
1172 if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
1173 spec->hp_independent_mode)
1174 snd_hda_codec_setup_stream(codec, mout->hp_nid,
1175 stream_tag, 0, format);
1176 }
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001177 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001178 return 0;
1179}
1180
1181static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1182 struct hda_codec *codec,
1183 struct snd_pcm_substream *substream)
1184{
1185 struct via_spec *spec = codec->spec;
1186 struct hda_multi_out *mout = &spec->multiout;
1187 hda_nid_t *nids = mout->dac_nids;
1188 int i;
1189
1190 if (substream->number == 0) {
1191 for (i = 0; i < mout->num_dacs; i++)
1192 snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
1193
1194 if (mout->hp_nid && !spec->hp_independent_mode)
1195 snd_hda_codec_setup_stream(codec, mout->hp_nid,
1196 0, 0, 0);
1197
1198 for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
1199 if (mout->extra_out_nid[i])
1200 snd_hda_codec_setup_stream(codec,
1201 mout->extra_out_nid[i],
1202 0, 0, 0);
1203 mutex_lock(&codec->spdif_mutex);
1204 if (mout->dig_out_nid &&
1205 mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
1206 snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
1207 0, 0, 0);
1208 mout->dig_out_used = 0;
1209 }
1210 mutex_unlock(&codec->spdif_mutex);
1211 } else {
1212 if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
1213 spec->hp_independent_mode)
1214 snd_hda_codec_setup_stream(codec, mout->hp_nid,
1215 0, 0, 0);
1216 }
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001217 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001218 return 0;
1219}
1220
Joseph Chanc577b8a2006-11-29 15:29:40 +01001221/*
1222 * Digital out
1223 */
1224static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1225 struct hda_codec *codec,
1226 struct snd_pcm_substream *substream)
1227{
1228 struct via_spec *spec = codec->spec;
1229 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1230}
1231
1232static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1233 struct hda_codec *codec,
1234 struct snd_pcm_substream *substream)
1235{
1236 struct via_spec *spec = codec->spec;
1237 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1238}
1239
Harald Welte5691ec72008-09-15 22:42:26 +08001240static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001241 struct hda_codec *codec,
1242 unsigned int stream_tag,
1243 unsigned int format,
1244 struct snd_pcm_substream *substream)
1245{
1246 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001247 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1248 stream_tag, format, substream);
1249}
Harald Welte5691ec72008-09-15 22:42:26 +08001250
Takashi Iwai9da29272009-05-07 16:31:14 +02001251static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1252 struct hda_codec *codec,
1253 struct snd_pcm_substream *substream)
1254{
1255 struct via_spec *spec = codec->spec;
1256 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001257 return 0;
1258}
1259
Joseph Chanc577b8a2006-11-29 15:29:40 +01001260/*
1261 * Analog capture
1262 */
1263static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1264 struct hda_codec *codec,
1265 unsigned int stream_tag,
1266 unsigned int format,
1267 struct snd_pcm_substream *substream)
1268{
1269 struct via_spec *spec = codec->spec;
1270
1271 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1272 stream_tag, 0, format);
1273 return 0;
1274}
1275
1276static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1277 struct hda_codec *codec,
1278 struct snd_pcm_substream *substream)
1279{
1280 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001281 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001282 return 0;
1283}
1284
1285static struct hda_pcm_stream vt1708_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08001286 .substreams = 2,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001287 .channels_min = 2,
1288 .channels_max = 8,
1289 .nid = 0x10, /* NID to query formats and rates */
1290 .ops = {
1291 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001292 .prepare = via_playback_multi_pcm_prepare,
1293 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001294 },
1295};
1296
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001297static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Lydia Wangc873cc22009-10-10 19:08:21 +08001298 .substreams = 2,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001299 .channels_min = 2,
1300 .channels_max = 8,
1301 .nid = 0x10, /* NID to query formats and rates */
1302 /* We got noisy outputs on the right channel on VT1708 when
1303 * 24bit samples are used. Until any workaround is found,
1304 * disable the 24bit format, so far.
1305 */
1306 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1307 .ops = {
1308 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08001309 .prepare = via_playback_multi_pcm_prepare,
1310 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001311 },
1312};
1313
Joseph Chanc577b8a2006-11-29 15:29:40 +01001314static struct hda_pcm_stream vt1708_pcm_analog_capture = {
1315 .substreams = 2,
1316 .channels_min = 2,
1317 .channels_max = 2,
1318 .nid = 0x15, /* NID to query formats and rates */
1319 .ops = {
1320 .prepare = via_capture_pcm_prepare,
1321 .cleanup = via_capture_pcm_cleanup
1322 },
1323};
1324
1325static struct hda_pcm_stream vt1708_pcm_digital_playback = {
1326 .substreams = 1,
1327 .channels_min = 2,
1328 .channels_max = 2,
1329 /* NID is set in via_build_pcms */
1330 .ops = {
1331 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001332 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001333 .prepare = via_dig_playback_pcm_prepare,
1334 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001335 },
1336};
1337
1338static struct hda_pcm_stream vt1708_pcm_digital_capture = {
1339 .substreams = 1,
1340 .channels_min = 2,
1341 .channels_max = 2,
1342};
1343
1344static int via_build_controls(struct hda_codec *codec)
1345{
1346 struct via_spec *spec = codec->spec;
1347 int err;
1348 int i;
1349
1350 for (i = 0; i < spec->num_mixers; i++) {
1351 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1352 if (err < 0)
1353 return err;
1354 }
1355
1356 if (spec->multiout.dig_out_nid) {
1357 err = snd_hda_create_spdif_out_ctls(codec,
1358 spec->multiout.dig_out_nid);
1359 if (err < 0)
1360 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001361 err = snd_hda_create_spdif_share_sw(codec,
1362 &spec->multiout);
1363 if (err < 0)
1364 return err;
1365 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001366 }
1367 if (spec->dig_in_nid) {
1368 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1369 if (err < 0)
1370 return err;
1371 }
Lydia Wang17314372009-10-10 19:07:37 +08001372
1373 /* init power states */
1374 set_jack_power_state(codec);
1375 analog_low_current_mode(codec, 1);
1376
Takashi Iwai603c4012008-07-30 15:01:44 +02001377 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001378 return 0;
1379}
1380
1381static int via_build_pcms(struct hda_codec *codec)
1382{
1383 struct via_spec *spec = codec->spec;
1384 struct hda_pcm *info = spec->pcm_rec;
1385
1386 codec->num_pcms = 1;
1387 codec->pcm_info = info;
1388
1389 info->name = spec->stream_name_analog;
1390 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
1391 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
1392 info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1393 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1394
1395 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1396 spec->multiout.max_channels;
1397
1398 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1399 codec->num_pcms++;
1400 info++;
1401 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001402 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001403 if (spec->multiout.dig_out_nid) {
1404 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1405 *(spec->stream_digital_playback);
1406 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1407 spec->multiout.dig_out_nid;
1408 }
1409 if (spec->dig_in_nid) {
1410 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1411 *(spec->stream_digital_capture);
1412 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1413 spec->dig_in_nid;
1414 }
1415 }
1416
1417 return 0;
1418}
1419
1420static void via_free(struct hda_codec *codec)
1421{
1422 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001423
1424 if (!spec)
1425 return;
1426
Takashi Iwai603c4012008-07-30 15:01:44 +02001427 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001428 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001429 kfree(codec->spec);
1430}
1431
Harald Welte69e52a82008-09-09 15:57:32 +08001432/* mute internal speaker if HP is plugged */
1433static void via_hp_automute(struct hda_codec *codec)
1434{
Lydia Wangdcf34c82009-10-10 19:08:15 +08001435 unsigned int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001436 struct via_spec *spec = codec->spec;
1437
1438 present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
1439 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
Lydia Wangdcf34c82009-10-10 19:08:15 +08001440
1441 if (!spec->hp_independent_mode) {
1442 struct snd_ctl_elem_id id;
1443 /* auto mute */
1444 snd_hda_codec_amp_stereo(
1445 codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
1446 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
1447 /* notify change */
1448 memset(&id, 0, sizeof(id));
1449 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
1450 strcpy(id.name, "Front Playback Switch");
1451 snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
1452 &id);
1453 }
Harald Welte69e52a82008-09-09 15:57:32 +08001454}
1455
1456static void via_gpio_control(struct hda_codec *codec)
1457{
1458 unsigned int gpio_data;
1459 unsigned int vol_counter;
1460 unsigned int vol;
1461 unsigned int master_vol;
1462
1463 struct via_spec *spec = codec->spec;
1464
1465 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1466 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1467
1468 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1469 0xF84, 0) & 0x3F0000) >> 16;
1470
1471 vol = vol_counter & 0x1F;
1472 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1473 AC_VERB_GET_AMP_GAIN_MUTE,
1474 AC_AMP_GET_INPUT);
1475
1476 if (gpio_data == 0x02) {
1477 /* unmute line out */
1478 snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
1479 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
1480
1481 if (vol_counter & 0x20) {
1482 /* decrease volume */
1483 if (vol > master_vol)
1484 vol = master_vol;
1485 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1486 0, HDA_AMP_VOLMASK,
1487 master_vol-vol);
1488 } else {
1489 /* increase volume */
1490 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1491 HDA_AMP_VOLMASK,
1492 ((master_vol+vol) > 0x2A) ? 0x2A :
1493 (master_vol+vol));
1494 }
1495 } else if (!(gpio_data & 0x02)) {
1496 /* mute line out */
1497 snd_hda_codec_amp_stereo(codec,
1498 spec->autocfg.line_out_pins[0],
1499 HDA_OUTPUT, 0, HDA_AMP_MUTE,
1500 HDA_AMP_MUTE);
1501 }
1502}
1503
1504/* unsolicited event for jack sensing */
1505static void via_unsol_event(struct hda_codec *codec,
1506 unsigned int res)
1507{
1508 res >>= 26;
Lydia Wanga34df192009-10-10 19:08:01 +08001509 if (res & VIA_HP_EVENT)
Harald Welte69e52a82008-09-09 15:57:32 +08001510 via_hp_automute(codec);
Lydia Wanga34df192009-10-10 19:08:01 +08001511 if (res & VIA_GPIO_EVENT)
Harald Welte69e52a82008-09-09 15:57:32 +08001512 via_gpio_control(codec);
Lydia Wanga34df192009-10-10 19:08:01 +08001513 if (res & VIA_JACK_EVENT)
1514 set_jack_power_state(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001515}
1516
Joseph Chanc577b8a2006-11-29 15:29:40 +01001517static int via_init(struct hda_codec *codec)
1518{
1519 struct via_spec *spec = codec->spec;
Harald Welte69e52a82008-09-09 15:57:32 +08001520 int i;
1521 for (i = 0; i < spec->num_iverbs; i++)
1522 snd_hda_sequence_write(codec, spec->init_verbs[i]);
1523
Lydia Wang518bf3b2009-10-10 19:07:29 +08001524 spec->codec_type = get_codec_type(codec);
1525 if (spec->codec_type == VT1708BCE)
1526 spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost
1527 same */
Josepch Chanf7278fd2007-12-13 16:40:40 +01001528 /* Lydia Add for EAPD enable */
1529 if (!spec->dig_in_nid) { /* No Digital In connection */
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02001530 if (spec->dig_in_pin) {
1531 snd_hda_codec_write(codec, spec->dig_in_pin, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001532 AC_VERB_SET_PIN_WIDGET_CONTROL,
Takashi Iwai12b74c82008-01-15 12:39:38 +01001533 PIN_OUT);
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02001534 snd_hda_codec_write(codec, spec->dig_in_pin, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001535 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
1536 }
Takashi Iwai12b74c82008-01-15 12:39:38 +01001537 } else /* enable SPDIF-input pin */
1538 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
1539 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
Josepch Chanf7278fd2007-12-13 16:40:40 +01001540
Takashi Iwai9da29272009-05-07 16:31:14 +02001541 /* assign slave outs */
1542 if (spec->slave_dig_outs[0])
1543 codec->slave_dig_outs = spec->slave_dig_outs;
Harald Welte5691ec72008-09-15 22:42:26 +08001544
Joseph Chanc577b8a2006-11-29 15:29:40 +01001545 return 0;
1546}
1547
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001548#ifdef SND_HDA_NEEDS_RESUME
1549static int via_suspend(struct hda_codec *codec, pm_message_t state)
1550{
1551 struct via_spec *spec = codec->spec;
1552 vt1708_stop_hp_work(spec);
1553 return 0;
1554}
1555#endif
1556
Takashi Iwaicb53c622007-08-10 17:21:45 +02001557#ifdef CONFIG_SND_HDA_POWER_SAVE
1558static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1559{
1560 struct via_spec *spec = codec->spec;
1561 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1562}
1563#endif
1564
Joseph Chanc577b8a2006-11-29 15:29:40 +01001565/*
1566 */
1567static struct hda_codec_ops via_patch_ops = {
1568 .build_controls = via_build_controls,
1569 .build_pcms = via_build_pcms,
1570 .init = via_init,
1571 .free = via_free,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001572#ifdef SND_HDA_NEEDS_RESUME
1573 .suspend = via_suspend,
1574#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001575#ifdef CONFIG_SND_HDA_POWER_SAVE
1576 .check_power_status = via_check_power_status,
1577#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001578};
1579
1580/* fill in the dac_nids table from the parsed pin configuration */
1581static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
1582 const struct auto_pin_cfg *cfg)
1583{
1584 int i;
1585 hda_nid_t nid;
1586
1587 spec->multiout.num_dacs = cfg->line_outs;
1588
1589 spec->multiout.dac_nids = spec->private_dac_nids;
1590
1591 for(i = 0; i < 4; i++) {
1592 nid = cfg->line_out_pins[i];
1593 if (nid) {
1594 /* config dac list */
1595 switch (i) {
1596 case AUTO_SEQ_FRONT:
1597 spec->multiout.dac_nids[i] = 0x10;
1598 break;
1599 case AUTO_SEQ_CENLFE:
1600 spec->multiout.dac_nids[i] = 0x12;
1601 break;
1602 case AUTO_SEQ_SURROUND:
Harald Weltefb4cb772008-09-09 15:53:36 +08001603 spec->multiout.dac_nids[i] = 0x11;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001604 break;
1605 case AUTO_SEQ_SIDE:
Harald Weltefb4cb772008-09-09 15:53:36 +08001606 spec->multiout.dac_nids[i] = 0x13;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001607 break;
1608 }
1609 }
1610 }
1611
1612 return 0;
1613}
1614
1615/* add playback controls from the parsed DAC table */
1616static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
1617 const struct auto_pin_cfg *cfg)
1618{
1619 char name[32];
1620 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
Lydia Wang9645c202009-10-10 19:08:27 +08001621 hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
Joseph Chanc577b8a2006-11-29 15:29:40 +01001622 int i, err;
1623
1624 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
1625 nid = cfg->line_out_pins[i];
1626
1627 if (!nid)
1628 continue;
1629
Lydia Wang9645c202009-10-10 19:08:27 +08001630 nid_vol = nid_vols[i];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001631
1632 if (i == AUTO_SEQ_CENLFE) {
1633 /* Center/LFE */
1634 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001635 "Center Playback Volume",
1636 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
1637 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001638 if (err < 0)
1639 return err;
1640 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1641 "LFE Playback Volume",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001642 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
1643 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001644 if (err < 0)
1645 return err;
1646 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1647 "Center Playback Switch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001648 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
1649 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001650 if (err < 0)
1651 return err;
1652 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1653 "LFE Playback Switch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001654 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
1655 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001656 if (err < 0)
1657 return err;
1658 } else if (i == AUTO_SEQ_FRONT){
1659 /* add control to mixer index 0 */
1660 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1661 "Master Front Playback Volume",
Lydia Wang9645c202009-10-10 19:08:27 +08001662 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001663 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001664 if (err < 0)
1665 return err;
1666 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1667 "Master Front Playback Switch",
Lydia Wang9645c202009-10-10 19:08:27 +08001668 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001669 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001670 if (err < 0)
1671 return err;
1672
1673 /* add control to PW3 */
1674 sprintf(name, "%s Playback Volume", chname[i]);
1675 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001676 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1677 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001678 if (err < 0)
1679 return err;
1680 sprintf(name, "%s Playback Switch", chname[i]);
1681 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001682 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1683 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001684 if (err < 0)
1685 return err;
1686 } else {
1687 sprintf(name, "%s Playback Volume", chname[i]);
1688 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001689 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1690 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001691 if (err < 0)
1692 return err;
1693 sprintf(name, "%s Playback Switch", chname[i]);
1694 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001695 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1696 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001697 if (err < 0)
1698 return err;
1699 }
1700 }
1701
1702 return 0;
1703}
1704
Harald Welte0aa62ae2008-09-09 15:58:27 +08001705static void create_hp_imux(struct via_spec *spec)
1706{
1707 int i;
1708 struct hda_input_mux *imux = &spec->private_imux[1];
1709 static const char *texts[] = { "OFF", "ON", NULL};
1710
1711 /* for hp mode select */
1712 i = 0;
1713 while (texts[i] != NULL) {
1714 imux->items[imux->num_items].label = texts[i];
1715 imux->items[imux->num_items].index = i;
1716 imux->num_items++;
1717 i++;
1718 }
1719
1720 spec->hp_mux = &spec->private_imux[1];
1721}
1722
Joseph Chanc577b8a2006-11-29 15:29:40 +01001723static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
1724{
1725 int err;
1726
1727 if (!pin)
1728 return 0;
1729
1730 spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
Lydia Wangcdc17842009-10-10 19:07:47 +08001731 spec->hp_independent_mode_index = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001732
1733 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1734 "Headphone Playback Volume",
1735 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
1736 if (err < 0)
1737 return err;
1738 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1739 "Headphone Playback Switch",
1740 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
1741 if (err < 0)
1742 return err;
1743
Harald Welte0aa62ae2008-09-09 15:58:27 +08001744 create_hp_imux(spec);
1745
Joseph Chanc577b8a2006-11-29 15:29:40 +01001746 return 0;
1747}
1748
1749/* create playback/capture controls for input pins */
1750static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
1751 const struct auto_pin_cfg *cfg)
1752{
1753 static char *labels[] = {
1754 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
1755 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08001756 struct hda_input_mux *imux = &spec->private_imux[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001757 int i, err, idx = 0;
1758
1759 /* for internal loopback recording select */
1760 imux->items[imux->num_items].label = "Stereo Mixer";
1761 imux->items[imux->num_items].index = idx;
1762 imux->num_items++;
1763
1764 for (i = 0; i < AUTO_PIN_LAST; i++) {
1765 if (!cfg->input_pins[i])
1766 continue;
1767
1768 switch (cfg->input_pins[i]) {
1769 case 0x1d: /* Mic */
1770 idx = 2;
1771 break;
1772
1773 case 0x1e: /* Line In */
1774 idx = 3;
1775 break;
1776
1777 case 0x21: /* Front Mic */
1778 idx = 4;
1779 break;
1780
1781 case 0x24: /* CD */
1782 idx = 1;
1783 break;
1784 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08001785 err = via_new_analog_input(spec, labels[i], idx, 0x17);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001786 if (err < 0)
1787 return err;
1788 imux->items[imux->num_items].label = labels[i];
1789 imux->items[imux->num_items].index = idx;
1790 imux->num_items++;
1791 }
1792 return 0;
1793}
1794
Takashi Iwaicb53c622007-08-10 17:21:45 +02001795#ifdef CONFIG_SND_HDA_POWER_SAVE
1796static struct hda_amp_list vt1708_loopbacks[] = {
1797 { 0x17, HDA_INPUT, 1 },
1798 { 0x17, HDA_INPUT, 2 },
1799 { 0x17, HDA_INPUT, 3 },
1800 { 0x17, HDA_INPUT, 4 },
1801 { } /* end */
1802};
1803#endif
1804
Harald Welte76d9b0d2008-09-09 15:50:37 +08001805static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
1806{
1807 unsigned int def_conf;
1808 unsigned char seqassoc;
1809
Takashi Iwai2f334f92009-02-20 14:37:42 +01001810 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08001811 seqassoc = (unsigned char) get_defcfg_association(def_conf);
1812 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08001813 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
1814 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
1815 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
1816 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08001817 }
1818
1819 return;
1820}
1821
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001822static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
1823 struct snd_ctl_elem_value *ucontrol)
1824{
1825 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1826 struct via_spec *spec = codec->spec;
1827
1828 if (spec->codec_type != VT1708)
1829 return 0;
1830 spec->vt1708_jack_detectect =
1831 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
1832 ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
1833 return 0;
1834}
1835
1836static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol,
1837 struct snd_ctl_elem_value *ucontrol)
1838{
1839 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1840 struct via_spec *spec = codec->spec;
1841 int change;
1842
1843 if (spec->codec_type != VT1708)
1844 return 0;
1845 spec->vt1708_jack_detectect = ucontrol->value.integer.value[0];
1846 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
1847 == !spec->vt1708_jack_detectect;
1848 if (spec->vt1708_jack_detectect) {
1849 mute_aa_path(codec, 1);
1850 notify_aa_path_ctls(codec);
1851 }
1852 return change;
1853}
1854
1855static struct snd_kcontrol_new vt1708_jack_detectect[] = {
1856 {
1857 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1858 .name = "Jack Detect",
1859 .count = 1,
1860 .info = snd_ctl_boolean_mono_info,
1861 .get = vt1708_jack_detectect_get,
1862 .put = vt1708_jack_detectect_put,
1863 },
1864 {} /* end */
1865};
1866
Joseph Chanc577b8a2006-11-29 15:29:40 +01001867static int vt1708_parse_auto_config(struct hda_codec *codec)
1868{
1869 struct via_spec *spec = codec->spec;
1870 int err;
1871
Harald Welte76d9b0d2008-09-09 15:50:37 +08001872 /* Add HP and CD pin config connect bit re-config action */
1873 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
1874 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
1875
Joseph Chanc577b8a2006-11-29 15:29:40 +01001876 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
1877 if (err < 0)
1878 return err;
1879 err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
1880 if (err < 0)
1881 return err;
1882 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
1883 return 0; /* can't find valid BIOS pin config */
1884
1885 err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
1886 if (err < 0)
1887 return err;
1888 err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
1889 if (err < 0)
1890 return err;
1891 err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
1892 if (err < 0)
1893 return err;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001894 /* add jack detect on/off control */
1895 err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect);
1896 if (err < 0)
1897 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001898
1899 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
1900
Takashi Iwai0852d7a2009-02-11 11:35:15 +01001901 if (spec->autocfg.dig_outs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001902 spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02001903 spec->dig_in_pin = VT1708_DIGIN_PIN;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001904 if (spec->autocfg.dig_in_pin)
1905 spec->dig_in_nid = VT1708_DIGIN_NID;
1906
Takashi Iwai603c4012008-07-30 15:01:44 +02001907 if (spec->kctls.list)
1908 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001909
Harald Welte69e52a82008-09-09 15:57:32 +08001910 spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001911
Harald Welte0aa62ae2008-09-09 15:58:27 +08001912 spec->input_mux = &spec->private_imux[0];
1913
Harald Weltef8fdd492008-09-15 22:41:31 +08001914 if (spec->hp_mux)
1915 spec->mixers[spec->num_mixers++] = via_hp_mixer;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001916
Lydia Wang1564b282009-10-10 19:07:52 +08001917 spec->mixers[spec->num_mixers++] = via_smart51_mixer;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001918 return 1;
1919}
1920
1921/* init callback for auto-configuration model -- overriding the default init */
1922static int via_auto_init(struct hda_codec *codec)
1923{
1924 via_init(codec);
1925 via_auto_init_multi_out(codec);
1926 via_auto_init_hp_out(codec);
1927 via_auto_init_analog_input(codec);
1928 return 0;
1929}
1930
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001931static void vt1708_update_hp_jack_state(struct work_struct *work)
1932{
1933 struct via_spec *spec = container_of(work, struct via_spec,
1934 vt1708_hp_work.work);
1935 if (spec->codec_type != VT1708)
1936 return;
1937 /* if jack state toggled */
1938 if (spec->vt1708_hp_present
1939 != (snd_hda_codec_read(spec->codec, spec->autocfg.hp_pins[0], 0,
1940 AC_VERB_GET_PIN_SENSE, 0) >> 31)) {
1941 spec->vt1708_hp_present ^= 1;
1942 via_hp_automute(spec->codec);
1943 }
1944 vt1708_start_hp_work(spec);
1945}
1946
Takashi Iwai337b9d02009-07-07 18:18:59 +02001947static int get_mux_nids(struct hda_codec *codec)
1948{
1949 struct via_spec *spec = codec->spec;
1950 hda_nid_t nid, conn[8];
1951 unsigned int type;
1952 int i, n;
1953
1954 for (i = 0; i < spec->num_adc_nids; i++) {
1955 nid = spec->adc_nids[i];
1956 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02001957 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02001958 if (type == AC_WID_PIN)
1959 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02001960 n = snd_hda_get_connections(codec, nid, conn,
1961 ARRAY_SIZE(conn));
1962 if (n <= 0)
1963 break;
1964 if (n > 1) {
1965 spec->mux_nids[i] = nid;
1966 break;
1967 }
1968 nid = conn[0];
1969 }
1970 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02001971 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02001972}
1973
Joseph Chanc577b8a2006-11-29 15:29:40 +01001974static int patch_vt1708(struct hda_codec *codec)
1975{
1976 struct via_spec *spec;
1977 int err;
1978
1979 /* create a codec specific record */
Harald Welteeb14a462008-09-09 15:40:38 +08001980 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001981 if (spec == NULL)
1982 return -ENOMEM;
1983
1984 codec->spec = spec;
1985
1986 /* automatic parse from the BIOS config */
1987 err = vt1708_parse_auto_config(codec);
1988 if (err < 0) {
1989 via_free(codec);
1990 return err;
1991 } else if (!err) {
1992 printk(KERN_INFO "hda_codec: Cannot set up configuration "
1993 "from BIOS. Using genenic mode...\n");
1994 }
1995
1996
1997 spec->stream_name_analog = "VT1708 Analog";
1998 spec->stream_analog_playback = &vt1708_pcm_analog_playback;
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001999 /* disable 32bit format on VT1708 */
2000 if (codec->vendor_id == 0x11061708)
2001 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002002 spec->stream_analog_capture = &vt1708_pcm_analog_capture;
2003
2004 spec->stream_name_digital = "VT1708 Digital";
2005 spec->stream_digital_playback = &vt1708_pcm_digital_playback;
2006 spec->stream_digital_capture = &vt1708_pcm_digital_capture;
2007
2008
2009 if (!spec->adc_nids && spec->input_mux) {
2010 spec->adc_nids = vt1708_adc_nids;
2011 spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
Takashi Iwai0f67a612009-08-31 08:12:29 +02002012 get_mux_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002013 spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
2014 spec->num_mixers++;
2015 }
2016
2017 codec->patch_ops = via_patch_ops;
2018
2019 codec->patch_ops.init = via_auto_init;
Takashi Iwaicb53c622007-08-10 17:21:45 +02002020#ifdef CONFIG_SND_HDA_POWER_SAVE
2021 spec->loopback.amplist = vt1708_loopbacks;
2022#endif
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002023 spec->codec = codec;
2024 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002025 return 0;
2026}
2027
2028/* capture mixer elements */
2029static struct snd_kcontrol_new vt1709_capture_mixer[] = {
2030 HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
2031 HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
2032 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
2033 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
2034 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
2035 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
2036 {
2037 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2038 /* The multiple "Capture Source" controls confuse alsamixer
2039 * So call somewhat different..
Joseph Chanc577b8a2006-11-29 15:29:40 +01002040 */
2041 /* .name = "Capture Source", */
2042 .name = "Input Source",
2043 .count = 1,
2044 .info = via_mux_enum_info,
2045 .get = via_mux_enum_get,
2046 .put = via_mux_enum_put,
2047 },
2048 { } /* end */
2049};
2050
Harald Welte69e52a82008-09-09 15:57:32 +08002051static struct hda_verb vt1709_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08002052 {0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
2053 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08002054 { }
2055};
2056
Joseph Chanc577b8a2006-11-29 15:29:40 +01002057/*
2058 * generic initialization of ADC, input mixers and output mixers
2059 */
2060static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
2061 /*
2062 * Unmute ADC0-2 and set the default input to mic-in
2063 */
2064 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2065 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2066 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2067
2068
Josepch Chanf7278fd2007-12-13 16:40:40 +01002069 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
Joseph Chanc577b8a2006-11-29 15:29:40 +01002070 * mixer widget
2071 */
2072 /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
Josepch Chanf7278fd2007-12-13 16:40:40 +01002073 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2074 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2075 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2076 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2077 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
Joseph Chanc577b8a2006-11-29 15:29:40 +01002078
2079 /*
2080 * Set up output selector (0x1a, 0x1b, 0x29)
2081 */
2082 /* set vol=0 to output mixers */
2083 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2084 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2085 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2086
2087 /*
2088 * Unmute PW3 and PW4
2089 */
2090 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2091 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2092
2093 /* Set input of PW4 as AOW4 */
2094 {0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
Joseph Chanc577b8a2006-11-29 15:29:40 +01002095 /* PW9 Output enable */
2096 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2097 { }
2098};
2099
2100static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
2101 .substreams = 1,
2102 .channels_min = 2,
2103 .channels_max = 10,
2104 .nid = 0x10, /* NID to query formats and rates */
2105 .ops = {
2106 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08002107 .prepare = via_playback_multi_pcm_prepare,
2108 .cleanup = via_playback_multi_pcm_cleanup,
Joseph Chanc577b8a2006-11-29 15:29:40 +01002109 },
2110};
2111
2112static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
2113 .substreams = 1,
2114 .channels_min = 2,
2115 .channels_max = 6,
2116 .nid = 0x10, /* NID to query formats and rates */
2117 .ops = {
2118 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08002119 .prepare = via_playback_multi_pcm_prepare,
2120 .cleanup = via_playback_multi_pcm_cleanup,
Joseph Chanc577b8a2006-11-29 15:29:40 +01002121 },
2122};
2123
2124static struct hda_pcm_stream vt1709_pcm_analog_capture = {
2125 .substreams = 2,
2126 .channels_min = 2,
2127 .channels_max = 2,
2128 .nid = 0x14, /* NID to query formats and rates */
2129 .ops = {
2130 .prepare = via_capture_pcm_prepare,
2131 .cleanup = via_capture_pcm_cleanup
2132 },
2133};
2134
2135static struct hda_pcm_stream vt1709_pcm_digital_playback = {
2136 .substreams = 1,
2137 .channels_min = 2,
2138 .channels_max = 2,
2139 /* NID is set in via_build_pcms */
2140 .ops = {
2141 .open = via_dig_playback_pcm_open,
2142 .close = via_dig_playback_pcm_close
2143 },
2144};
2145
2146static struct hda_pcm_stream vt1709_pcm_digital_capture = {
2147 .substreams = 1,
2148 .channels_min = 2,
2149 .channels_max = 2,
2150};
2151
2152static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
2153 const struct auto_pin_cfg *cfg)
2154{
2155 int i;
2156 hda_nid_t nid;
2157
2158 if (cfg->line_outs == 4) /* 10 channels */
2159 spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
2160 else if (cfg->line_outs == 3) /* 6 channels */
2161 spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
2162
2163 spec->multiout.dac_nids = spec->private_dac_nids;
2164
2165 if (cfg->line_outs == 4) { /* 10 channels */
2166 for (i = 0; i < cfg->line_outs; i++) {
2167 nid = cfg->line_out_pins[i];
2168 if (nid) {
2169 /* config dac list */
2170 switch (i) {
2171 case AUTO_SEQ_FRONT:
2172 /* AOW0 */
2173 spec->multiout.dac_nids[i] = 0x10;
2174 break;
2175 case AUTO_SEQ_CENLFE:
2176 /* AOW2 */
2177 spec->multiout.dac_nids[i] = 0x12;
2178 break;
2179 case AUTO_SEQ_SURROUND:
2180 /* AOW3 */
Harald Weltefb4cb772008-09-09 15:53:36 +08002181 spec->multiout.dac_nids[i] = 0x11;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002182 break;
2183 case AUTO_SEQ_SIDE:
2184 /* AOW1 */
Harald Weltefb4cb772008-09-09 15:53:36 +08002185 spec->multiout.dac_nids[i] = 0x27;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002186 break;
2187 default:
2188 break;
2189 }
2190 }
2191 }
2192 spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
2193
2194 } else if (cfg->line_outs == 3) { /* 6 channels */
2195 for(i = 0; i < cfg->line_outs; i++) {
2196 nid = cfg->line_out_pins[i];
2197 if (nid) {
2198 /* config dac list */
2199 switch(i) {
2200 case AUTO_SEQ_FRONT:
2201 /* AOW0 */
2202 spec->multiout.dac_nids[i] = 0x10;
2203 break;
2204 case AUTO_SEQ_CENLFE:
2205 /* AOW2 */
2206 spec->multiout.dac_nids[i] = 0x12;
2207 break;
2208 case AUTO_SEQ_SURROUND:
2209 /* AOW1 */
2210 spec->multiout.dac_nids[i] = 0x11;
2211 break;
2212 default:
2213 break;
2214 }
2215 }
2216 }
2217 }
2218
2219 return 0;
2220}
2221
2222/* add playback controls from the parsed DAC table */
2223static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
2224 const struct auto_pin_cfg *cfg)
2225{
2226 char name[32];
2227 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
Lydia Wang4483a2f2009-10-10 19:08:29 +08002228 hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
Joseph Chanc577b8a2006-11-29 15:29:40 +01002229 int i, err;
2230
2231 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2232 nid = cfg->line_out_pins[i];
2233
2234 if (!nid)
2235 continue;
2236
Lydia Wang4483a2f2009-10-10 19:08:29 +08002237 nid_vol = nid_vols[i];
2238
Joseph Chanc577b8a2006-11-29 15:29:40 +01002239 if (i == AUTO_SEQ_CENLFE) {
2240 /* Center/LFE */
2241 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2242 "Center Playback Volume",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002243 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002244 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002245 if (err < 0)
2246 return err;
2247 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2248 "LFE Playback Volume",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002249 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002250 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002251 if (err < 0)
2252 return err;
2253 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2254 "Center Playback Switch",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002255 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002256 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002257 if (err < 0)
2258 return err;
2259 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2260 "LFE Playback Switch",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002261 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002262 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002263 if (err < 0)
2264 return err;
2265 } else if (i == AUTO_SEQ_FRONT){
Lydia Wang4483a2f2009-10-10 19:08:29 +08002266 /* ADD control to mixer index 0 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002267 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2268 "Master Front Playback Volume",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002269 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002270 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002271 if (err < 0)
2272 return err;
2273 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2274 "Master Front Playback Switch",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002275 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002276 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002277 if (err < 0)
2278 return err;
2279
2280 /* add control to PW3 */
2281 sprintf(name, "%s Playback Volume", chname[i]);
2282 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002283 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2284 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002285 if (err < 0)
2286 return err;
2287 sprintf(name, "%s Playback Switch", chname[i]);
2288 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002289 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2290 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002291 if (err < 0)
2292 return err;
2293 } else if (i == AUTO_SEQ_SURROUND) {
2294 sprintf(name, "%s Playback Volume", chname[i]);
2295 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002296 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002297 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002298 if (err < 0)
2299 return err;
2300 sprintf(name, "%s Playback Switch", chname[i]);
2301 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002302 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002303 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002304 if (err < 0)
2305 return err;
2306 } else if (i == AUTO_SEQ_SIDE) {
2307 sprintf(name, "%s Playback Volume", chname[i]);
2308 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002309 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002310 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002311 if (err < 0)
2312 return err;
2313 sprintf(name, "%s Playback Switch", chname[i]);
2314 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002315 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002316 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002317 if (err < 0)
2318 return err;
2319 }
2320 }
2321
2322 return 0;
2323}
2324
2325static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2326{
2327 int err;
2328
2329 if (!pin)
2330 return 0;
2331
2332 if (spec->multiout.num_dacs == 5) /* 10 channels */
2333 spec->multiout.hp_nid = VT1709_HP_DAC_NID;
2334 else if (spec->multiout.num_dacs == 3) /* 6 channels */
2335 spec->multiout.hp_nid = 0;
Lydia Wangcdc17842009-10-10 19:07:47 +08002336 spec->hp_independent_mode_index = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002337
2338 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2339 "Headphone Playback Volume",
2340 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2341 if (err < 0)
2342 return err;
2343 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2344 "Headphone Playback Switch",
2345 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2346 if (err < 0)
2347 return err;
2348
2349 return 0;
2350}
2351
2352/* create playback/capture controls for input pins */
2353static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
2354 const struct auto_pin_cfg *cfg)
2355{
2356 static char *labels[] = {
2357 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
2358 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08002359 struct hda_input_mux *imux = &spec->private_imux[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01002360 int i, err, idx = 0;
2361
2362 /* for internal loopback recording select */
2363 imux->items[imux->num_items].label = "Stereo Mixer";
2364 imux->items[imux->num_items].index = idx;
2365 imux->num_items++;
2366
2367 for (i = 0; i < AUTO_PIN_LAST; i++) {
2368 if (!cfg->input_pins[i])
2369 continue;
2370
2371 switch (cfg->input_pins[i]) {
2372 case 0x1d: /* Mic */
2373 idx = 2;
2374 break;
2375
2376 case 0x1e: /* Line In */
2377 idx = 3;
2378 break;
2379
2380 case 0x21: /* Front Mic */
2381 idx = 4;
2382 break;
2383
2384 case 0x23: /* CD */
2385 idx = 1;
2386 break;
2387 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08002388 err = via_new_analog_input(spec, labels[i], idx, 0x18);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002389 if (err < 0)
2390 return err;
2391 imux->items[imux->num_items].label = labels[i];
2392 imux->items[imux->num_items].index = idx;
2393 imux->num_items++;
2394 }
2395 return 0;
2396}
2397
2398static int vt1709_parse_auto_config(struct hda_codec *codec)
2399{
2400 struct via_spec *spec = codec->spec;
2401 int err;
2402
2403 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2404 if (err < 0)
2405 return err;
2406 err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
2407 if (err < 0)
2408 return err;
2409 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2410 return 0; /* can't find valid BIOS pin config */
2411
2412 err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
2413 if (err < 0)
2414 return err;
2415 err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2416 if (err < 0)
2417 return err;
2418 err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg);
2419 if (err < 0)
2420 return err;
2421
2422 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2423
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002424 if (spec->autocfg.dig_outs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002425 spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02002426 spec->dig_in_pin = VT1709_DIGIN_PIN;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002427 if (spec->autocfg.dig_in_pin)
2428 spec->dig_in_nid = VT1709_DIGIN_NID;
2429
Takashi Iwai603c4012008-07-30 15:01:44 +02002430 if (spec->kctls.list)
2431 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002432
Harald Welte0aa62ae2008-09-09 15:58:27 +08002433 spec->input_mux = &spec->private_imux[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01002434
Harald Weltef8fdd492008-09-15 22:41:31 +08002435 if (spec->hp_mux)
2436 spec->mixers[spec->num_mixers++] = via_hp_mixer;
2437
Lydia Wang1564b282009-10-10 19:07:52 +08002438 spec->mixers[spec->num_mixers++] = via_smart51_mixer;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002439 return 1;
2440}
2441
Takashi Iwaicb53c622007-08-10 17:21:45 +02002442#ifdef CONFIG_SND_HDA_POWER_SAVE
2443static struct hda_amp_list vt1709_loopbacks[] = {
2444 { 0x18, HDA_INPUT, 1 },
2445 { 0x18, HDA_INPUT, 2 },
2446 { 0x18, HDA_INPUT, 3 },
2447 { 0x18, HDA_INPUT, 4 },
2448 { } /* end */
2449};
2450#endif
2451
Joseph Chanc577b8a2006-11-29 15:29:40 +01002452static int patch_vt1709_10ch(struct hda_codec *codec)
2453{
2454 struct via_spec *spec;
2455 int err;
2456
2457 /* create a codec specific record */
Harald Welteeb14a462008-09-09 15:40:38 +08002458 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002459 if (spec == NULL)
2460 return -ENOMEM;
2461
2462 codec->spec = spec;
2463
2464 err = vt1709_parse_auto_config(codec);
2465 if (err < 0) {
2466 via_free(codec);
2467 return err;
2468 } else if (!err) {
2469 printk(KERN_INFO "hda_codec: Cannot set up configuration. "
2470 "Using genenic mode...\n");
2471 }
2472
Harald Welte69e52a82008-09-09 15:57:32 +08002473 spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
2474 spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002475
2476 spec->stream_name_analog = "VT1709 Analog";
2477 spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
2478 spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2479
2480 spec->stream_name_digital = "VT1709 Digital";
2481 spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2482 spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2483
2484
2485 if (!spec->adc_nids && spec->input_mux) {
2486 spec->adc_nids = vt1709_adc_nids;
2487 spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02002488 get_mux_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002489 spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2490 spec->num_mixers++;
2491 }
2492
2493 codec->patch_ops = via_patch_ops;
2494
2495 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08002496 codec->patch_ops.unsol_event = via_unsol_event;
Takashi Iwaicb53c622007-08-10 17:21:45 +02002497#ifdef CONFIG_SND_HDA_POWER_SAVE
2498 spec->loopback.amplist = vt1709_loopbacks;
2499#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01002500
2501 return 0;
2502}
2503/*
2504 * generic initialization of ADC, input mixers and output mixers
2505 */
2506static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
2507 /*
2508 * Unmute ADC0-2 and set the default input to mic-in
2509 */
2510 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2511 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2512 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2513
2514
2515 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2516 * mixer widget
2517 */
2518 /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2519 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2520 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2521 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2522 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2523 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2524
2525 /*
2526 * Set up output selector (0x1a, 0x1b, 0x29)
2527 */
2528 /* set vol=0 to output mixers */
2529 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2530 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2531 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2532
2533 /*
2534 * Unmute PW3 and PW4
2535 */
2536 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2537 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2538
2539 /* Set input of PW4 as MW0 */
2540 {0x20, AC_VERB_SET_CONNECT_SEL, 0},
Joseph Chanc577b8a2006-11-29 15:29:40 +01002541 /* PW9 Output enable */
2542 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2543 { }
2544};
2545
2546static int patch_vt1709_6ch(struct hda_codec *codec)
2547{
2548 struct via_spec *spec;
2549 int err;
2550
2551 /* create a codec specific record */
Harald Welteeb14a462008-09-09 15:40:38 +08002552 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002553 if (spec == NULL)
2554 return -ENOMEM;
2555
2556 codec->spec = spec;
2557
2558 err = vt1709_parse_auto_config(codec);
2559 if (err < 0) {
2560 via_free(codec);
2561 return err;
2562 } else if (!err) {
2563 printk(KERN_INFO "hda_codec: Cannot set up configuration. "
2564 "Using genenic mode...\n");
2565 }
2566
Harald Welte69e52a82008-09-09 15:57:32 +08002567 spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
2568 spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002569
2570 spec->stream_name_analog = "VT1709 Analog";
2571 spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
2572 spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2573
2574 spec->stream_name_digital = "VT1709 Digital";
2575 spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2576 spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2577
2578
2579 if (!spec->adc_nids && spec->input_mux) {
2580 spec->adc_nids = vt1709_adc_nids;
2581 spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02002582 get_mux_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002583 spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2584 spec->num_mixers++;
2585 }
2586
2587 codec->patch_ops = via_patch_ops;
2588
2589 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08002590 codec->patch_ops.unsol_event = via_unsol_event;
Takashi Iwaicb53c622007-08-10 17:21:45 +02002591#ifdef CONFIG_SND_HDA_POWER_SAVE
2592 spec->loopback.amplist = vt1709_loopbacks;
2593#endif
Josepch Chanf7278fd2007-12-13 16:40:40 +01002594 return 0;
2595}
2596
2597/* capture mixer elements */
2598static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
2599 HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
2600 HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
2601 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
2602 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
2603 {
2604 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2605 /* The multiple "Capture Source" controls confuse alsamixer
2606 * So call somewhat different..
Josepch Chanf7278fd2007-12-13 16:40:40 +01002607 */
2608 /* .name = "Capture Source", */
2609 .name = "Input Source",
2610 .count = 1,
2611 .info = via_mux_enum_info,
2612 .get = via_mux_enum_get,
2613 .put = via_mux_enum_put,
2614 },
2615 { } /* end */
2616};
2617/*
2618 * generic initialization of ADC, input mixers and output mixers
2619 */
2620static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
2621 /*
2622 * Unmute ADC0-1 and set the default input to mic-in
2623 */
2624 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2625 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2626
2627
2628 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2629 * mixer widget
2630 */
2631 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2632 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2633 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2634 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2635 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2636 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2637
2638 /*
2639 * Set up output mixers
2640 */
2641 /* set vol=0 to output mixers */
2642 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2643 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2644 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2645
2646 /* Setup default input to PW4 */
2647 {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1},
2648 /* PW9 Output enable */
2649 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2650 /* PW10 Input enable */
2651 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2652 { }
2653};
2654
2655static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
2656 /*
2657 * Unmute ADC0-1 and set the default input to mic-in
2658 */
2659 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2660 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2661
2662
2663 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2664 * mixer widget
2665 */
2666 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2667 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2668 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2669 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2670 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2671 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2672
2673 /*
2674 * Set up output mixers
2675 */
2676 /* set vol=0 to output mixers */
2677 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2678 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2679 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2680
2681 /* Setup default input of PW4 to MW0 */
2682 {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
2683 /* PW9 Output enable */
2684 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2685 /* PW10 Input enable */
2686 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2687 { }
2688};
2689
Harald Welte69e52a82008-09-09 15:57:32 +08002690static struct hda_verb vt1708B_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08002691 {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
2692 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
2693 {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2694 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2695 {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2696 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2697 {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2698 {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2699 {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08002700 { }
2701};
2702
Lydia Wang17314372009-10-10 19:07:37 +08002703static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
2704 struct hda_codec *codec,
2705 struct snd_pcm_substream *substream)
2706{
2707 int idle = substream->pstr->substream_opened == 1
2708 && substream->ref_count == 0;
2709
2710 analog_low_current_mode(codec, idle);
2711 return 0;
2712}
2713
Josepch Chanf7278fd2007-12-13 16:40:40 +01002714static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08002715 .substreams = 2,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002716 .channels_min = 2,
2717 .channels_max = 8,
2718 .nid = 0x10, /* NID to query formats and rates */
2719 .ops = {
2720 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08002721 .prepare = via_playback_multi_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08002722 .cleanup = via_playback_multi_pcm_cleanup,
2723 .close = via_pcm_open_close
Josepch Chanf7278fd2007-12-13 16:40:40 +01002724 },
2725};
2726
2727static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08002728 .substreams = 2,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002729 .channels_min = 2,
2730 .channels_max = 4,
2731 .nid = 0x10, /* NID to query formats and rates */
2732 .ops = {
2733 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08002734 .prepare = via_playback_multi_pcm_prepare,
2735 .cleanup = via_playback_multi_pcm_cleanup
Josepch Chanf7278fd2007-12-13 16:40:40 +01002736 },
2737};
2738
2739static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
2740 .substreams = 2,
2741 .channels_min = 2,
2742 .channels_max = 2,
2743 .nid = 0x13, /* NID to query formats and rates */
2744 .ops = {
Lydia Wang17314372009-10-10 19:07:37 +08002745 .open = via_pcm_open_close,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002746 .prepare = via_capture_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08002747 .cleanup = via_capture_pcm_cleanup,
2748 .close = via_pcm_open_close
Josepch Chanf7278fd2007-12-13 16:40:40 +01002749 },
2750};
2751
2752static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
2753 .substreams = 1,
2754 .channels_min = 2,
2755 .channels_max = 2,
2756 /* NID is set in via_build_pcms */
2757 .ops = {
2758 .open = via_dig_playback_pcm_open,
2759 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02002760 .prepare = via_dig_playback_pcm_prepare,
2761 .cleanup = via_dig_playback_pcm_cleanup
Josepch Chanf7278fd2007-12-13 16:40:40 +01002762 },
2763};
2764
2765static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
2766 .substreams = 1,
2767 .channels_min = 2,
2768 .channels_max = 2,
2769};
2770
2771/* fill in the dac_nids table from the parsed pin configuration */
2772static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
2773 const struct auto_pin_cfg *cfg)
2774{
2775 int i;
2776 hda_nid_t nid;
2777
2778 spec->multiout.num_dacs = cfg->line_outs;
2779
2780 spec->multiout.dac_nids = spec->private_dac_nids;
2781
2782 for (i = 0; i < 4; i++) {
2783 nid = cfg->line_out_pins[i];
2784 if (nid) {
2785 /* config dac list */
2786 switch (i) {
2787 case AUTO_SEQ_FRONT:
2788 spec->multiout.dac_nids[i] = 0x10;
2789 break;
2790 case AUTO_SEQ_CENLFE:
2791 spec->multiout.dac_nids[i] = 0x24;
2792 break;
2793 case AUTO_SEQ_SURROUND:
Harald Weltefb4cb772008-09-09 15:53:36 +08002794 spec->multiout.dac_nids[i] = 0x11;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002795 break;
2796 case AUTO_SEQ_SIDE:
Harald Weltefb4cb772008-09-09 15:53:36 +08002797 spec->multiout.dac_nids[i] = 0x25;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002798 break;
2799 }
2800 }
2801 }
2802
2803 return 0;
2804}
2805
2806/* add playback controls from the parsed DAC table */
2807static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
2808 const struct auto_pin_cfg *cfg)
2809{
2810 char name[32];
2811 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
Harald Weltefb4cb772008-09-09 15:53:36 +08002812 hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
Josepch Chanf7278fd2007-12-13 16:40:40 +01002813 hda_nid_t nid, nid_vol = 0;
2814 int i, err;
2815
2816 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2817 nid = cfg->line_out_pins[i];
2818
2819 if (!nid)
2820 continue;
2821
2822 nid_vol = nid_vols[i];
2823
2824 if (i == AUTO_SEQ_CENLFE) {
2825 /* Center/LFE */
2826 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2827 "Center Playback Volume",
2828 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2829 HDA_OUTPUT));
2830 if (err < 0)
2831 return err;
2832 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2833 "LFE Playback Volume",
2834 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2835 HDA_OUTPUT));
2836 if (err < 0)
2837 return err;
2838 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2839 "Center Playback Switch",
2840 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2841 HDA_OUTPUT));
2842 if (err < 0)
2843 return err;
2844 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2845 "LFE Playback Switch",
2846 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2847 HDA_OUTPUT));
2848 if (err < 0)
2849 return err;
2850 } else if (i == AUTO_SEQ_FRONT) {
2851 /* add control to mixer index 0 */
2852 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2853 "Master Front Playback Volume",
2854 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2855 HDA_INPUT));
2856 if (err < 0)
2857 return err;
2858 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2859 "Master Front Playback Switch",
2860 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2861 HDA_INPUT));
2862 if (err < 0)
2863 return err;
2864
2865 /* add control to PW3 */
2866 sprintf(name, "%s Playback Volume", chname[i]);
2867 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2868 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2869 HDA_OUTPUT));
2870 if (err < 0)
2871 return err;
2872 sprintf(name, "%s Playback Switch", chname[i]);
2873 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2874 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2875 HDA_OUTPUT));
2876 if (err < 0)
2877 return err;
2878 } else {
2879 sprintf(name, "%s Playback Volume", chname[i]);
2880 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2881 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2882 HDA_OUTPUT));
2883 if (err < 0)
2884 return err;
2885 sprintf(name, "%s Playback Switch", chname[i]);
2886 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2887 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2888 HDA_OUTPUT));
2889 if (err < 0)
2890 return err;
2891 }
2892 }
2893
2894 return 0;
2895}
2896
2897static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2898{
2899 int err;
2900
2901 if (!pin)
2902 return 0;
2903
2904 spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
Lydia Wangcdc17842009-10-10 19:07:47 +08002905 spec->hp_independent_mode_index = 1;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002906
2907 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2908 "Headphone Playback Volume",
2909 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2910 if (err < 0)
2911 return err;
2912 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2913 "Headphone Playback Switch",
2914 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2915 if (err < 0)
2916 return err;
2917
Harald Welte0aa62ae2008-09-09 15:58:27 +08002918 create_hp_imux(spec);
2919
Josepch Chanf7278fd2007-12-13 16:40:40 +01002920 return 0;
2921}
2922
2923/* create playback/capture controls for input pins */
2924static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
2925 const struct auto_pin_cfg *cfg)
2926{
2927 static char *labels[] = {
2928 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
2929 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08002930 struct hda_input_mux *imux = &spec->private_imux[0];
Josepch Chanf7278fd2007-12-13 16:40:40 +01002931 int i, err, idx = 0;
2932
2933 /* for internal loopback recording select */
2934 imux->items[imux->num_items].label = "Stereo Mixer";
2935 imux->items[imux->num_items].index = idx;
2936 imux->num_items++;
2937
2938 for (i = 0; i < AUTO_PIN_LAST; i++) {
2939 if (!cfg->input_pins[i])
2940 continue;
2941
2942 switch (cfg->input_pins[i]) {
2943 case 0x1a: /* Mic */
2944 idx = 2;
2945 break;
2946
2947 case 0x1b: /* Line In */
2948 idx = 3;
2949 break;
2950
2951 case 0x1e: /* Front Mic */
2952 idx = 4;
2953 break;
2954
2955 case 0x1f: /* CD */
2956 idx = 1;
2957 break;
2958 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08002959 err = via_new_analog_input(spec, labels[i], idx, 0x16);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002960 if (err < 0)
2961 return err;
2962 imux->items[imux->num_items].label = labels[i];
2963 imux->items[imux->num_items].index = idx;
2964 imux->num_items++;
2965 }
2966 return 0;
2967}
2968
2969static int vt1708B_parse_auto_config(struct hda_codec *codec)
2970{
2971 struct via_spec *spec = codec->spec;
2972 int err;
2973
2974 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2975 if (err < 0)
2976 return err;
2977 err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
2978 if (err < 0)
2979 return err;
2980 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2981 return 0; /* can't find valid BIOS pin config */
2982
2983 err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
2984 if (err < 0)
2985 return err;
2986 err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2987 if (err < 0)
2988 return err;
2989 err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
2990 if (err < 0)
2991 return err;
2992
2993 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2994
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002995 if (spec->autocfg.dig_outs)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002996 spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02002997 spec->dig_in_pin = VT1708B_DIGIN_PIN;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002998 if (spec->autocfg.dig_in_pin)
2999 spec->dig_in_nid = VT1708B_DIGIN_NID;
3000
Takashi Iwai603c4012008-07-30 15:01:44 +02003001 if (spec->kctls.list)
3002 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003003
Harald Welte0aa62ae2008-09-09 15:58:27 +08003004 spec->input_mux = &spec->private_imux[0];
3005
Harald Weltef8fdd492008-09-15 22:41:31 +08003006 if (spec->hp_mux)
3007 spec->mixers[spec->num_mixers++] = via_hp_mixer;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003008
Lydia Wang1564b282009-10-10 19:07:52 +08003009 spec->mixers[spec->num_mixers++] = via_smart51_mixer;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003010 return 1;
3011}
3012
3013#ifdef CONFIG_SND_HDA_POWER_SAVE
3014static struct hda_amp_list vt1708B_loopbacks[] = {
3015 { 0x16, HDA_INPUT, 1 },
3016 { 0x16, HDA_INPUT, 2 },
3017 { 0x16, HDA_INPUT, 3 },
3018 { 0x16, HDA_INPUT, 4 },
3019 { } /* end */
3020};
3021#endif
Lydia Wang518bf3b2009-10-10 19:07:29 +08003022static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003023static int patch_vt1708B_8ch(struct hda_codec *codec)
3024{
3025 struct via_spec *spec;
3026 int err;
3027
Lydia Wang518bf3b2009-10-10 19:07:29 +08003028 if (get_codec_type(codec) == VT1708BCE)
3029 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003030 /* create a codec specific record */
Harald Welteeb14a462008-09-09 15:40:38 +08003031 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003032 if (spec == NULL)
3033 return -ENOMEM;
3034
3035 codec->spec = spec;
3036
3037 /* automatic parse from the BIOS config */
3038 err = vt1708B_parse_auto_config(codec);
3039 if (err < 0) {
3040 via_free(codec);
3041 return err;
3042 } else if (!err) {
3043 printk(KERN_INFO "hda_codec: Cannot set up configuration "
3044 "from BIOS. Using genenic mode...\n");
3045 }
3046
Harald Welte69e52a82008-09-09 15:57:32 +08003047 spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
3048 spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003049
3050 spec->stream_name_analog = "VT1708B Analog";
3051 spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
3052 spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3053
3054 spec->stream_name_digital = "VT1708B Digital";
3055 spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3056 spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3057
3058 if (!spec->adc_nids && spec->input_mux) {
3059 spec->adc_nids = vt1708B_adc_nids;
3060 spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003061 get_mux_nids(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003062 spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3063 spec->num_mixers++;
3064 }
3065
3066 codec->patch_ops = via_patch_ops;
3067
3068 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003069 codec->patch_ops.unsol_event = via_unsol_event;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003070#ifdef CONFIG_SND_HDA_POWER_SAVE
3071 spec->loopback.amplist = vt1708B_loopbacks;
3072#endif
3073
3074 return 0;
3075}
3076
3077static int patch_vt1708B_4ch(struct hda_codec *codec)
3078{
3079 struct via_spec *spec;
3080 int err;
3081
3082 /* create a codec specific record */
Harald Welteeb14a462008-09-09 15:40:38 +08003083 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003084 if (spec == NULL)
3085 return -ENOMEM;
3086
3087 codec->spec = spec;
3088
3089 /* automatic parse from the BIOS config */
3090 err = vt1708B_parse_auto_config(codec);
3091 if (err < 0) {
3092 via_free(codec);
3093 return err;
3094 } else if (!err) {
3095 printk(KERN_INFO "hda_codec: Cannot set up configuration "
3096 "from BIOS. Using genenic mode...\n");
3097 }
3098
Harald Welte69e52a82008-09-09 15:57:32 +08003099 spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
3100 spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003101
3102 spec->stream_name_analog = "VT1708B Analog";
3103 spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
3104 spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3105
3106 spec->stream_name_digital = "VT1708B Digital";
3107 spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3108 spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3109
3110 if (!spec->adc_nids && spec->input_mux) {
3111 spec->adc_nids = vt1708B_adc_nids;
3112 spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003113 get_mux_nids(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003114 spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3115 spec->num_mixers++;
3116 }
3117
3118 codec->patch_ops = via_patch_ops;
3119
3120 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003121 codec->patch_ops.unsol_event = via_unsol_event;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003122#ifdef CONFIG_SND_HDA_POWER_SAVE
3123 spec->loopback.amplist = vt1708B_loopbacks;
3124#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01003125
3126 return 0;
3127}
3128
Harald Welted949cac2008-09-09 15:56:01 +08003129/* Patch for VT1708S */
3130
3131/* capture mixer elements */
3132static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
3133 HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3134 HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3135 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3136 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
Lydia Wang6369bcf2009-10-10 19:08:31 +08003137 HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
3138 HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
3139 HDA_INPUT),
Harald Welted949cac2008-09-09 15:56:01 +08003140 {
3141 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3142 /* The multiple "Capture Source" controls confuse alsamixer
3143 * So call somewhat different..
3144 */
3145 /* .name = "Capture Source", */
3146 .name = "Input Source",
3147 .count = 1,
3148 .info = via_mux_enum_info,
3149 .get = via_mux_enum_get,
3150 .put = via_mux_enum_put,
3151 },
3152 { } /* end */
3153};
3154
3155static struct hda_verb vt1708S_volume_init_verbs[] = {
3156 /* Unmute ADC0-1 and set the default input to mic-in */
3157 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3158 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3159
3160 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
3161 * analog-loopback mixer widget */
3162 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3163 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3164 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3165 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3166 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3167 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3168
3169 /* Setup default input of PW4 to MW0 */
3170 {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
Harald Welte5691ec72008-09-15 22:42:26 +08003171 /* PW9, PW10 Output enable */
Harald Welted949cac2008-09-09 15:56:01 +08003172 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Harald Welte5691ec72008-09-15 22:42:26 +08003173 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Harald Welted7426322008-09-15 22:43:23 +08003174 /* Enable Mic Boost Volume backdoor */
3175 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003176 /* don't bybass mixer */
3177 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08003178 { }
3179};
3180
Harald Welte69e52a82008-09-09 15:57:32 +08003181static struct hda_verb vt1708S_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08003182 {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3183 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3184 {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3185 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3186 {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3187 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3188 {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3189 {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3190 {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08003191 { }
3192};
3193
Harald Welted949cac2008-09-09 15:56:01 +08003194static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
3195 .substreams = 2,
3196 .channels_min = 2,
3197 .channels_max = 8,
3198 .nid = 0x10, /* NID to query formats and rates */
3199 .ops = {
3200 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08003201 .prepare = via_playback_multi_pcm_prepare,
3202 .cleanup = via_playback_multi_pcm_cleanup,
Lydia Wang17314372009-10-10 19:07:37 +08003203 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003204 },
3205};
3206
3207static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
3208 .substreams = 2,
3209 .channels_min = 2,
3210 .channels_max = 2,
3211 .nid = 0x13, /* NID to query formats and rates */
3212 .ops = {
Lydia Wang17314372009-10-10 19:07:37 +08003213 .open = via_pcm_open_close,
Harald Welted949cac2008-09-09 15:56:01 +08003214 .prepare = via_capture_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08003215 .cleanup = via_capture_pcm_cleanup,
3216 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003217 },
3218};
3219
3220static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
Takashi Iwai9da29272009-05-07 16:31:14 +02003221 .substreams = 1,
Harald Welted949cac2008-09-09 15:56:01 +08003222 .channels_min = 2,
3223 .channels_max = 2,
3224 /* NID is set in via_build_pcms */
3225 .ops = {
3226 .open = via_dig_playback_pcm_open,
3227 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02003228 .prepare = via_dig_playback_pcm_prepare,
3229 .cleanup = via_dig_playback_pcm_cleanup
Harald Welted949cac2008-09-09 15:56:01 +08003230 },
3231};
3232
3233/* fill in the dac_nids table from the parsed pin configuration */
3234static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
3235 const struct auto_pin_cfg *cfg)
3236{
3237 int i;
3238 hda_nid_t nid;
3239
3240 spec->multiout.num_dacs = cfg->line_outs;
3241
3242 spec->multiout.dac_nids = spec->private_dac_nids;
3243
3244 for (i = 0; i < 4; i++) {
3245 nid = cfg->line_out_pins[i];
3246 if (nid) {
3247 /* config dac list */
3248 switch (i) {
3249 case AUTO_SEQ_FRONT:
3250 spec->multiout.dac_nids[i] = 0x10;
3251 break;
3252 case AUTO_SEQ_CENLFE:
3253 spec->multiout.dac_nids[i] = 0x24;
3254 break;
3255 case AUTO_SEQ_SURROUND:
3256 spec->multiout.dac_nids[i] = 0x11;
3257 break;
3258 case AUTO_SEQ_SIDE:
3259 spec->multiout.dac_nids[i] = 0x25;
3260 break;
3261 }
3262 }
3263 }
3264
3265 return 0;
3266}
3267
3268/* add playback controls from the parsed DAC table */
3269static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
3270 const struct auto_pin_cfg *cfg)
3271{
3272 char name[32];
3273 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
3274 hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25};
3275 hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27};
3276 hda_nid_t nid, nid_vol, nid_mute;
3277 int i, err;
3278
3279 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3280 nid = cfg->line_out_pins[i];
3281
3282 if (!nid)
3283 continue;
3284
3285 nid_vol = nid_vols[i];
3286 nid_mute = nid_mutes[i];
3287
3288 if (i == AUTO_SEQ_CENLFE) {
3289 /* Center/LFE */
3290 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3291 "Center Playback Volume",
3292 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3293 HDA_OUTPUT));
3294 if (err < 0)
3295 return err;
3296 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3297 "LFE Playback Volume",
3298 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3299 HDA_OUTPUT));
3300 if (err < 0)
3301 return err;
3302 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3303 "Center Playback Switch",
3304 HDA_COMPOSE_AMP_VAL(nid_mute,
3305 1, 0,
3306 HDA_OUTPUT));
3307 if (err < 0)
3308 return err;
3309 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3310 "LFE Playback Switch",
3311 HDA_COMPOSE_AMP_VAL(nid_mute,
3312 2, 0,
3313 HDA_OUTPUT));
3314 if (err < 0)
3315 return err;
3316 } else if (i == AUTO_SEQ_FRONT) {
3317 /* add control to mixer index 0 */
3318 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3319 "Master Front Playback Volume",
3320 HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3321 HDA_INPUT));
3322 if (err < 0)
3323 return err;
3324 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3325 "Master Front Playback Switch",
3326 HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3327 HDA_INPUT));
3328 if (err < 0)
3329 return err;
3330
3331 /* Front */
3332 sprintf(name, "%s Playback Volume", chname[i]);
3333 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3334 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3335 HDA_OUTPUT));
3336 if (err < 0)
3337 return err;
3338 sprintf(name, "%s Playback Switch", chname[i]);
3339 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3340 HDA_COMPOSE_AMP_VAL(nid_mute,
3341 3, 0,
3342 HDA_OUTPUT));
3343 if (err < 0)
3344 return err;
3345 } else {
3346 sprintf(name, "%s Playback Volume", chname[i]);
3347 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3348 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3349 HDA_OUTPUT));
3350 if (err < 0)
3351 return err;
3352 sprintf(name, "%s Playback Switch", chname[i]);
3353 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3354 HDA_COMPOSE_AMP_VAL(nid_mute,
3355 3, 0,
3356 HDA_OUTPUT));
3357 if (err < 0)
3358 return err;
3359 }
3360 }
3361
3362 return 0;
3363}
3364
3365static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3366{
3367 int err;
3368
3369 if (!pin)
3370 return 0;
3371
3372 spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
Lydia Wangcdc17842009-10-10 19:07:47 +08003373 spec->hp_independent_mode_index = 1;
Harald Welted949cac2008-09-09 15:56:01 +08003374
3375 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3376 "Headphone Playback Volume",
3377 HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
3378 if (err < 0)
3379 return err;
3380
3381 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3382 "Headphone Playback Switch",
3383 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3384 if (err < 0)
3385 return err;
3386
Harald Welte0aa62ae2008-09-09 15:58:27 +08003387 create_hp_imux(spec);
3388
Harald Welted949cac2008-09-09 15:56:01 +08003389 return 0;
3390}
3391
3392/* create playback/capture controls for input pins */
3393static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
3394 const struct auto_pin_cfg *cfg)
3395{
3396 static char *labels[] = {
3397 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
3398 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08003399 struct hda_input_mux *imux = &spec->private_imux[0];
Harald Welted949cac2008-09-09 15:56:01 +08003400 int i, err, idx = 0;
3401
3402 /* for internal loopback recording select */
3403 imux->items[imux->num_items].label = "Stereo Mixer";
3404 imux->items[imux->num_items].index = 5;
3405 imux->num_items++;
3406
3407 for (i = 0; i < AUTO_PIN_LAST; i++) {
3408 if (!cfg->input_pins[i])
3409 continue;
3410
3411 switch (cfg->input_pins[i]) {
3412 case 0x1a: /* Mic */
3413 idx = 2;
3414 break;
3415
3416 case 0x1b: /* Line In */
3417 idx = 3;
3418 break;
3419
3420 case 0x1e: /* Front Mic */
3421 idx = 4;
3422 break;
3423
3424 case 0x1f: /* CD */
3425 idx = 1;
3426 break;
3427 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08003428 err = via_new_analog_input(spec, labels[i], idx, 0x16);
Harald Welted949cac2008-09-09 15:56:01 +08003429 if (err < 0)
3430 return err;
3431 imux->items[imux->num_items].label = labels[i];
3432 imux->items[imux->num_items].index = idx-1;
3433 imux->num_items++;
3434 }
3435 return 0;
3436}
3437
Takashi Iwai9da29272009-05-07 16:31:14 +02003438/* fill out digital output widgets; one for master and one for slave outputs */
3439static void fill_dig_outs(struct hda_codec *codec)
3440{
3441 struct via_spec *spec = codec->spec;
3442 int i;
3443
3444 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3445 hda_nid_t nid;
3446 int conn;
3447
3448 nid = spec->autocfg.dig_out_pins[i];
3449 if (!nid)
3450 continue;
3451 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3452 if (conn < 1)
3453 continue;
3454 if (!spec->multiout.dig_out_nid)
3455 spec->multiout.dig_out_nid = nid;
3456 else {
3457 spec->slave_dig_outs[0] = nid;
3458 break; /* at most two dig outs */
3459 }
3460 }
3461}
3462
Harald Welted949cac2008-09-09 15:56:01 +08003463static int vt1708S_parse_auto_config(struct hda_codec *codec)
3464{
3465 struct via_spec *spec = codec->spec;
3466 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003467
Takashi Iwai9da29272009-05-07 16:31:14 +02003468 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
Harald Welted949cac2008-09-09 15:56:01 +08003469 if (err < 0)
3470 return err;
3471 err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
3472 if (err < 0)
3473 return err;
3474 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3475 return 0; /* can't find valid BIOS pin config */
3476
3477 err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg);
3478 if (err < 0)
3479 return err;
3480 err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3481 if (err < 0)
3482 return err;
3483 err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg);
3484 if (err < 0)
3485 return err;
3486
3487 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3488
Takashi Iwai9da29272009-05-07 16:31:14 +02003489 fill_dig_outs(codec);
Harald Welte98aa34c2008-09-09 16:02:09 +08003490
Takashi Iwai603c4012008-07-30 15:01:44 +02003491 if (spec->kctls.list)
3492 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Harald Welted949cac2008-09-09 15:56:01 +08003493
Harald Welte0aa62ae2008-09-09 15:58:27 +08003494 spec->input_mux = &spec->private_imux[0];
3495
Harald Weltef8fdd492008-09-15 22:41:31 +08003496 if (spec->hp_mux)
3497 spec->mixers[spec->num_mixers++] = via_hp_mixer;
Harald Welted949cac2008-09-09 15:56:01 +08003498
Lydia Wang1564b282009-10-10 19:07:52 +08003499 spec->mixers[spec->num_mixers++] = via_smart51_mixer;
Harald Welted949cac2008-09-09 15:56:01 +08003500 return 1;
3501}
3502
3503#ifdef CONFIG_SND_HDA_POWER_SAVE
3504static struct hda_amp_list vt1708S_loopbacks[] = {
3505 { 0x16, HDA_INPUT, 1 },
3506 { 0x16, HDA_INPUT, 2 },
3507 { 0x16, HDA_INPUT, 3 },
3508 { 0x16, HDA_INPUT, 4 },
3509 { } /* end */
3510};
3511#endif
3512
Lydia Wang6369bcf2009-10-10 19:08:31 +08003513static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3514 int offset, int num_steps, int step_size)
3515{
3516 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3517 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3518 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3519 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3520 (0 << AC_AMPCAP_MUTE_SHIFT));
3521}
3522
Harald Welted949cac2008-09-09 15:56:01 +08003523static int patch_vt1708S(struct hda_codec *codec)
3524{
3525 struct via_spec *spec;
3526 int err;
3527
3528 /* create a codec specific record */
3529 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3530 if (spec == NULL)
3531 return -ENOMEM;
3532
3533 codec->spec = spec;
3534
3535 /* automatic parse from the BIOS config */
3536 err = vt1708S_parse_auto_config(codec);
3537 if (err < 0) {
3538 via_free(codec);
3539 return err;
3540 } else if (!err) {
3541 printk(KERN_INFO "hda_codec: Cannot set up configuration "
3542 "from BIOS. Using genenic mode...\n");
3543 }
3544
Harald Welte69e52a82008-09-09 15:57:32 +08003545 spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
3546 spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003547
3548 spec->stream_name_analog = "VT1708S Analog";
3549 spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
3550 spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
3551
3552 spec->stream_name_digital = "VT1708S Digital";
3553 spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
3554
3555 if (!spec->adc_nids && spec->input_mux) {
3556 spec->adc_nids = vt1708S_adc_nids;
3557 spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003558 get_mux_nids(codec);
Lydia Wang6369bcf2009-10-10 19:08:31 +08003559 override_mic_boost(codec, 0x1a, 0, 3, 40);
3560 override_mic_boost(codec, 0x1e, 0, 3, 40);
Harald Welted949cac2008-09-09 15:56:01 +08003561 spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
3562 spec->num_mixers++;
3563 }
3564
3565 codec->patch_ops = via_patch_ops;
3566
3567 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003568 codec->patch_ops.unsol_event = via_unsol_event;
Harald Welted949cac2008-09-09 15:56:01 +08003569#ifdef CONFIG_SND_HDA_POWER_SAVE
3570 spec->loopback.amplist = vt1708S_loopbacks;
3571#endif
3572
Lydia Wang518bf3b2009-10-10 19:07:29 +08003573 /* correct names for VT1708BCE */
3574 if (get_codec_type(codec) == VT1708BCE) {
3575 kfree(codec->chip_name);
3576 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3577 snprintf(codec->bus->card->mixername,
3578 sizeof(codec->bus->card->mixername),
3579 "%s %s", codec->vendor_name, codec->chip_name);
3580 spec->stream_name_analog = "VT1708BCE Analog";
3581 spec->stream_name_digital = "VT1708BCE Digital";
3582 }
Harald Welted949cac2008-09-09 15:56:01 +08003583 return 0;
3584}
3585
3586/* Patch for VT1702 */
3587
3588/* capture mixer elements */
3589static struct snd_kcontrol_new vt1702_capture_mixer[] = {
3590 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
3591 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
3592 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
3593 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
3594 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
3595 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
3596 HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
3597 HDA_INPUT),
3598 {
3599 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3600 /* The multiple "Capture Source" controls confuse alsamixer
3601 * So call somewhat different..
3602 */
3603 /* .name = "Capture Source", */
3604 .name = "Input Source",
3605 .count = 1,
3606 .info = via_mux_enum_info,
3607 .get = via_mux_enum_get,
3608 .put = via_mux_enum_put,
3609 },
3610 { } /* end */
3611};
3612
3613static struct hda_verb vt1702_volume_init_verbs[] = {
3614 /*
3615 * Unmute ADC0-1 and set the default input to mic-in
3616 */
3617 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3618 {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3619 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3620
3621
3622 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3623 * mixer widget
3624 */
3625 /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
3626 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3627 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3628 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3629 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3630 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3631
3632 /* Setup default input of PW4 to MW0 */
3633 {0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
3634 /* PW6 PW7 Output enable */
3635 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3636 {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003637 /* mixer enable */
3638 {0x1, 0xF88, 0x3},
3639 /* GPIO 0~2 */
3640 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003641 { }
3642};
3643
Harald Welte69e52a82008-09-09 15:57:32 +08003644static struct hda_verb vt1702_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08003645 {0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
3646 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3647 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3648 {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3649 {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3650 {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08003651 { }
3652};
3653
Harald Welted949cac2008-09-09 15:56:01 +08003654static struct hda_pcm_stream vt1702_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08003655 .substreams = 2,
Harald Welted949cac2008-09-09 15:56:01 +08003656 .channels_min = 2,
3657 .channels_max = 2,
3658 .nid = 0x10, /* NID to query formats and rates */
3659 .ops = {
3660 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08003661 .prepare = via_playback_multi_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08003662 .cleanup = via_playback_multi_pcm_cleanup,
3663 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003664 },
3665};
3666
3667static struct hda_pcm_stream vt1702_pcm_analog_capture = {
3668 .substreams = 3,
3669 .channels_min = 2,
3670 .channels_max = 2,
3671 .nid = 0x12, /* NID to query formats and rates */
3672 .ops = {
Lydia Wang17314372009-10-10 19:07:37 +08003673 .open = via_pcm_open_close,
Harald Welted949cac2008-09-09 15:56:01 +08003674 .prepare = via_capture_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08003675 .cleanup = via_capture_pcm_cleanup,
3676 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003677 },
3678};
3679
3680static struct hda_pcm_stream vt1702_pcm_digital_playback = {
Harald Welte5691ec72008-09-15 22:42:26 +08003681 .substreams = 2,
Harald Welted949cac2008-09-09 15:56:01 +08003682 .channels_min = 2,
3683 .channels_max = 2,
3684 /* NID is set in via_build_pcms */
3685 .ops = {
3686 .open = via_dig_playback_pcm_open,
3687 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02003688 .prepare = via_dig_playback_pcm_prepare,
3689 .cleanup = via_dig_playback_pcm_cleanup
Harald Welted949cac2008-09-09 15:56:01 +08003690 },
3691};
3692
3693/* fill in the dac_nids table from the parsed pin configuration */
3694static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
3695 const struct auto_pin_cfg *cfg)
3696{
3697 spec->multiout.num_dacs = 1;
3698 spec->multiout.dac_nids = spec->private_dac_nids;
3699
3700 if (cfg->line_out_pins[0]) {
3701 /* config dac list */
3702 spec->multiout.dac_nids[0] = 0x10;
3703 }
3704
3705 return 0;
3706}
3707
3708/* add playback controls from the parsed DAC table */
3709static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
3710 const struct auto_pin_cfg *cfg)
3711{
3712 int err;
3713
3714 if (!cfg->line_out_pins[0])
3715 return -1;
3716
3717 /* add control to mixer index 0 */
3718 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3719 "Master Front Playback Volume",
3720 HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
3721 if (err < 0)
3722 return err;
3723 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3724 "Master Front Playback Switch",
3725 HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
3726 if (err < 0)
3727 return err;
3728
3729 /* Front */
3730 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3731 "Front Playback Volume",
3732 HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
3733 if (err < 0)
3734 return err;
3735 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3736 "Front Playback Switch",
3737 HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
3738 if (err < 0)
3739 return err;
3740
3741 return 0;
3742}
3743
3744static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3745{
Lydia Wang0713efe2009-10-10 19:07:43 +08003746 int err, i;
3747 struct hda_input_mux *imux;
3748 static const char *texts[] = { "ON", "OFF", NULL};
Harald Welted949cac2008-09-09 15:56:01 +08003749 if (!pin)
3750 return 0;
Harald Welted949cac2008-09-09 15:56:01 +08003751 spec->multiout.hp_nid = 0x1D;
Lydia Wangcdc17842009-10-10 19:07:47 +08003752 spec->hp_independent_mode_index = 0;
Harald Welted949cac2008-09-09 15:56:01 +08003753
3754 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3755 "Headphone Playback Volume",
3756 HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
3757 if (err < 0)
3758 return err;
3759
3760 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3761 "Headphone Playback Switch",
3762 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3763 if (err < 0)
3764 return err;
3765
Lydia Wang0713efe2009-10-10 19:07:43 +08003766 imux = &spec->private_imux[1];
Harald Welte0aa62ae2008-09-09 15:58:27 +08003767
Lydia Wang0713efe2009-10-10 19:07:43 +08003768 /* for hp mode select */
3769 i = 0;
3770 while (texts[i] != NULL) {
3771 imux->items[imux->num_items].label = texts[i];
3772 imux->items[imux->num_items].index = i;
3773 imux->num_items++;
3774 i++;
3775 }
3776
3777 spec->hp_mux = &spec->private_imux[1];
Harald Welted949cac2008-09-09 15:56:01 +08003778 return 0;
3779}
3780
3781/* create playback/capture controls for input pins */
3782static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
3783 const struct auto_pin_cfg *cfg)
3784{
3785 static char *labels[] = {
3786 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
3787 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08003788 struct hda_input_mux *imux = &spec->private_imux[0];
Harald Welted949cac2008-09-09 15:56:01 +08003789 int i, err, idx = 0;
3790
3791 /* for internal loopback recording select */
3792 imux->items[imux->num_items].label = "Stereo Mixer";
3793 imux->items[imux->num_items].index = 3;
3794 imux->num_items++;
3795
3796 for (i = 0; i < AUTO_PIN_LAST; i++) {
3797 if (!cfg->input_pins[i])
3798 continue;
3799
3800 switch (cfg->input_pins[i]) {
3801 case 0x14: /* Mic */
3802 idx = 1;
3803 break;
3804
3805 case 0x15: /* Line In */
3806 idx = 2;
3807 break;
3808
3809 case 0x18: /* Front Mic */
3810 idx = 3;
3811 break;
3812 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08003813 err = via_new_analog_input(spec, labels[i], idx, 0x1A);
Harald Welted949cac2008-09-09 15:56:01 +08003814 if (err < 0)
3815 return err;
3816 imux->items[imux->num_items].label = labels[i];
3817 imux->items[imux->num_items].index = idx-1;
3818 imux->num_items++;
3819 }
3820 return 0;
3821}
3822
3823static int vt1702_parse_auto_config(struct hda_codec *codec)
3824{
3825 struct via_spec *spec = codec->spec;
3826 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003827
Takashi Iwai9da29272009-05-07 16:31:14 +02003828 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
Harald Welted949cac2008-09-09 15:56:01 +08003829 if (err < 0)
3830 return err;
3831 err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
3832 if (err < 0)
3833 return err;
3834 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3835 return 0; /* can't find valid BIOS pin config */
3836
3837 err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
3838 if (err < 0)
3839 return err;
3840 err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3841 if (err < 0)
3842 return err;
Lydia Wangc2c02ea2009-10-10 19:07:32 +08003843 /* limit AA path volume to 0 dB */
3844 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3845 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3846 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3847 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3848 (1 << AC_AMPCAP_MUTE_SHIFT));
Harald Welted949cac2008-09-09 15:56:01 +08003849 err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
3850 if (err < 0)
3851 return err;
3852
3853 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3854
Takashi Iwai9da29272009-05-07 16:31:14 +02003855 fill_dig_outs(codec);
Harald Welte98aa34c2008-09-09 16:02:09 +08003856
Takashi Iwai603c4012008-07-30 15:01:44 +02003857 if (spec->kctls.list)
3858 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Harald Welted949cac2008-09-09 15:56:01 +08003859
Harald Welte0aa62ae2008-09-09 15:58:27 +08003860 spec->input_mux = &spec->private_imux[0];
3861
Harald Weltef8fdd492008-09-15 22:41:31 +08003862 if (spec->hp_mux)
3863 spec->mixers[spec->num_mixers++] = via_hp_mixer;
Harald Welted949cac2008-09-09 15:56:01 +08003864
3865 return 1;
3866}
3867
3868#ifdef CONFIG_SND_HDA_POWER_SAVE
3869static struct hda_amp_list vt1702_loopbacks[] = {
3870 { 0x1A, HDA_INPUT, 1 },
3871 { 0x1A, HDA_INPUT, 2 },
3872 { 0x1A, HDA_INPUT, 3 },
3873 { 0x1A, HDA_INPUT, 4 },
3874 { } /* end */
3875};
3876#endif
3877
3878static int patch_vt1702(struct hda_codec *codec)
3879{
3880 struct via_spec *spec;
3881 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003882
3883 /* create a codec specific record */
3884 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3885 if (spec == NULL)
3886 return -ENOMEM;
3887
3888 codec->spec = spec;
3889
3890 /* automatic parse from the BIOS config */
3891 err = vt1702_parse_auto_config(codec);
3892 if (err < 0) {
3893 via_free(codec);
3894 return err;
3895 } else if (!err) {
3896 printk(KERN_INFO "hda_codec: Cannot set up configuration "
3897 "from BIOS. Using genenic mode...\n");
3898 }
3899
Harald Welte69e52a82008-09-09 15:57:32 +08003900 spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
3901 spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003902
3903 spec->stream_name_analog = "VT1702 Analog";
3904 spec->stream_analog_playback = &vt1702_pcm_analog_playback;
3905 spec->stream_analog_capture = &vt1702_pcm_analog_capture;
3906
3907 spec->stream_name_digital = "VT1702 Digital";
3908 spec->stream_digital_playback = &vt1702_pcm_digital_playback;
3909
3910 if (!spec->adc_nids && spec->input_mux) {
3911 spec->adc_nids = vt1702_adc_nids;
3912 spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003913 get_mux_nids(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003914 spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
3915 spec->num_mixers++;
3916 }
3917
3918 codec->patch_ops = via_patch_ops;
3919
3920 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003921 codec->patch_ops.unsol_event = via_unsol_event;
Harald Welted949cac2008-09-09 15:56:01 +08003922#ifdef CONFIG_SND_HDA_POWER_SAVE
3923 spec->loopback.amplist = vt1702_loopbacks;
3924#endif
3925
Harald Welted949cac2008-09-09 15:56:01 +08003926 return 0;
3927}
3928
Lydia Wangeb7188c2009-10-10 19:08:34 +08003929/* Patch for VT1718S */
3930
3931/* capture mixer elements */
3932static struct snd_kcontrol_new vt1718S_capture_mixer[] = {
3933 HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
3934 HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
3935 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
3936 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
3937 HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
3938 HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
3939 HDA_INPUT),
3940 {
3941 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3942 /* The multiple "Capture Source" controls confuse alsamixer
3943 * So call somewhat different..
3944 */
3945 .name = "Input Source",
3946 .count = 2,
3947 .info = via_mux_enum_info,
3948 .get = via_mux_enum_get,
3949 .put = via_mux_enum_put,
3950 },
3951 { } /* end */
3952};
3953
3954static struct hda_verb vt1718S_volume_init_verbs[] = {
3955 /*
3956 * Unmute ADC0-1 and set the default input to mic-in
3957 */
3958 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3959 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3960
3961
3962 /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3963 * mixer widget
3964 */
3965 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3966 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3967 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3968 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3969 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3970 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3971
3972 /* Setup default input of Front HP to MW9 */
3973 {0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
3974 /* PW9 PW10 Output enable */
3975 {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
3976 {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
3977 /* PW11 Input enable */
3978 {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
3979 /* Enable Boost Volume backdoor */
3980 {0x1, 0xf88, 0x8},
3981 /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
3982 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3983 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3984 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3985 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3986 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3987 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3988 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3989 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3990 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3991 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3992 /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
3993 {0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
3994 {0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
3995 /* Unmute MW4's index 0 */
3996 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3997 { }
3998};
3999
4000
4001static struct hda_verb vt1718S_uniwill_init_verbs[] = {
4002 {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
4003 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4004 {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4005 {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4006 {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4007 {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4008 {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4009 {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4010 {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4011 { }
4012};
4013
4014static struct hda_pcm_stream vt1718S_pcm_analog_playback = {
4015 .substreams = 2,
4016 .channels_min = 2,
4017 .channels_max = 10,
4018 .nid = 0x8, /* NID to query formats and rates */
4019 .ops = {
4020 .open = via_playback_pcm_open,
4021 .prepare = via_playback_multi_pcm_prepare,
4022 .cleanup = via_playback_multi_pcm_cleanup,
4023 .close = via_pcm_open_close,
4024 },
4025};
4026
4027static struct hda_pcm_stream vt1718S_pcm_analog_capture = {
4028 .substreams = 2,
4029 .channels_min = 2,
4030 .channels_max = 2,
4031 .nid = 0x10, /* NID to query formats and rates */
4032 .ops = {
4033 .open = via_pcm_open_close,
4034 .prepare = via_capture_pcm_prepare,
4035 .cleanup = via_capture_pcm_cleanup,
4036 .close = via_pcm_open_close,
4037 },
4038};
4039
4040static struct hda_pcm_stream vt1718S_pcm_digital_playback = {
4041 .substreams = 2,
4042 .channels_min = 2,
4043 .channels_max = 2,
4044 .rates = SNDRV_PCM_RATE_48000,
4045 /* NID is set in via_build_pcms */
4046 .ops = {
4047 .open = via_dig_playback_pcm_open,
4048 .close = via_dig_playback_pcm_close,
4049 .prepare = via_dig_playback_pcm_prepare,
4050 .cleanup = via_dig_playback_pcm_cleanup
4051 },
4052};
4053
4054static struct hda_pcm_stream vt1718S_pcm_digital_capture = {
4055 .substreams = 1,
4056 .channels_min = 2,
4057 .channels_max = 2,
4058};
4059
4060/* fill in the dac_nids table from the parsed pin configuration */
4061static int vt1718S_auto_fill_dac_nids(struct via_spec *spec,
4062 const struct auto_pin_cfg *cfg)
4063{
4064 int i;
4065 hda_nid_t nid;
4066
4067 spec->multiout.num_dacs = cfg->line_outs;
4068
4069 spec->multiout.dac_nids = spec->private_dac_nids;
4070
4071 for (i = 0; i < 4; i++) {
4072 nid = cfg->line_out_pins[i];
4073 if (nid) {
4074 /* config dac list */
4075 switch (i) {
4076 case AUTO_SEQ_FRONT:
4077 spec->multiout.dac_nids[i] = 0x8;
4078 break;
4079 case AUTO_SEQ_CENLFE:
4080 spec->multiout.dac_nids[i] = 0xa;
4081 break;
4082 case AUTO_SEQ_SURROUND:
4083 spec->multiout.dac_nids[i] = 0x9;
4084 break;
4085 case AUTO_SEQ_SIDE:
4086 spec->multiout.dac_nids[i] = 0xb;
4087 break;
4088 }
4089 }
4090 }
4091
4092 return 0;
4093}
4094
4095/* add playback controls from the parsed DAC table */
4096static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec,
4097 const struct auto_pin_cfg *cfg)
4098{
4099 char name[32];
4100 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
4101 hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb};
4102 hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27};
4103 hda_nid_t nid, nid_vol, nid_mute = 0;
4104 int i, err;
4105
4106 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
4107 nid = cfg->line_out_pins[i];
4108
4109 if (!nid)
4110 continue;
4111 nid_vol = nid_vols[i];
4112 nid_mute = nid_mutes[i];
4113
4114 if (i == AUTO_SEQ_CENLFE) {
4115 /* Center/LFE */
4116 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4117 "Center Playback Volume",
4118 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
4119 HDA_OUTPUT));
4120 if (err < 0)
4121 return err;
4122 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4123 "LFE Playback Volume",
4124 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
4125 HDA_OUTPUT));
4126 if (err < 0)
4127 return err;
4128 err = via_add_control(
4129 spec, VIA_CTL_WIDGET_MUTE,
4130 "Center Playback Switch",
4131 HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
4132 HDA_OUTPUT));
4133 if (err < 0)
4134 return err;
4135 err = via_add_control(
4136 spec, VIA_CTL_WIDGET_MUTE,
4137 "LFE Playback Switch",
4138 HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
4139 HDA_OUTPUT));
4140 if (err < 0)
4141 return err;
4142 } else if (i == AUTO_SEQ_FRONT) {
4143 /* Front */
4144 sprintf(name, "%s Playback Volume", chname[i]);
4145 err = via_add_control(
4146 spec, VIA_CTL_WIDGET_VOL, name,
4147 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4148 if (err < 0)
4149 return err;
4150 sprintf(name, "%s Playback Switch", chname[i]);
4151 err = via_add_control(
4152 spec, VIA_CTL_WIDGET_MUTE, name,
4153 HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4154 HDA_OUTPUT));
4155 if (err < 0)
4156 return err;
4157 } else {
4158 sprintf(name, "%s Playback Volume", chname[i]);
4159 err = via_add_control(
4160 spec, VIA_CTL_WIDGET_VOL, name,
4161 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4162 if (err < 0)
4163 return err;
4164 sprintf(name, "%s Playback Switch", chname[i]);
4165 err = via_add_control(
4166 spec, VIA_CTL_WIDGET_MUTE, name,
4167 HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4168 HDA_OUTPUT));
4169 if (err < 0)
4170 return err;
4171 }
4172 }
4173 return 0;
4174}
4175
4176static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4177{
4178 int err;
4179
4180 if (!pin)
4181 return 0;
4182
4183 spec->multiout.hp_nid = 0xc; /* AOW4 */
4184 spec->hp_independent_mode_index = 1;
4185
4186 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4187 "Headphone Playback Volume",
4188 HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT));
4189 if (err < 0)
4190 return err;
4191
4192 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4193 "Headphone Playback Switch",
4194 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4195 if (err < 0)
4196 return err;
4197
4198 create_hp_imux(spec);
4199 return 0;
4200}
4201
4202/* create playback/capture controls for input pins */
4203static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec,
4204 const struct auto_pin_cfg *cfg)
4205{
4206 static char *labels[] = {
4207 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
4208 };
4209 struct hda_input_mux *imux = &spec->private_imux[0];
4210 int i, err, idx = 0;
4211
4212 /* for internal loopback recording select */
4213 imux->items[imux->num_items].label = "Stereo Mixer";
4214 imux->items[imux->num_items].index = 5;
4215 imux->num_items++;
4216
4217 for (i = 0; i < AUTO_PIN_LAST; i++) {
4218 if (!cfg->input_pins[i])
4219 continue;
4220
4221 switch (cfg->input_pins[i]) {
4222 case 0x2b: /* Mic */
4223 idx = 1;
4224 break;
4225
4226 case 0x2a: /* Line In */
4227 idx = 2;
4228 break;
4229
4230 case 0x29: /* Front Mic */
4231 idx = 3;
4232 break;
4233
4234 case 0x2c: /* CD */
4235 idx = 0;
4236 break;
4237 }
4238 err = via_new_analog_input(spec, labels[i], idx, 0x21);
4239 if (err < 0)
4240 return err;
4241 imux->items[imux->num_items].label = labels[i];
4242 imux->items[imux->num_items].index = idx;
4243 imux->num_items++;
4244 }
4245 return 0;
4246}
4247
4248static int vt1718S_parse_auto_config(struct hda_codec *codec)
4249{
4250 struct via_spec *spec = codec->spec;
4251 int err;
4252
4253 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4254
4255 if (err < 0)
4256 return err;
4257 err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg);
4258 if (err < 0)
4259 return err;
4260 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4261 return 0; /* can't find valid BIOS pin config */
4262
4263 err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg);
4264 if (err < 0)
4265 return err;
4266 err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4267 if (err < 0)
4268 return err;
4269 err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg);
4270 if (err < 0)
4271 return err;
4272
4273 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4274
4275 fill_dig_outs(codec);
4276
4277 if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
4278 spec->dig_in_nid = 0x13;
4279
4280 if (spec->kctls.list)
4281 spec->mixers[spec->num_mixers++] = spec->kctls.list;
4282
4283 spec->input_mux = &spec->private_imux[0];
4284
4285 if (spec->hp_mux)
4286 spec->mixers[spec->num_mixers++] = via_hp_mixer;
4287
4288 spec->mixers[spec->num_mixers++] = via_smart51_mixer;
4289
4290 return 1;
4291}
4292
4293#ifdef CONFIG_SND_HDA_POWER_SAVE
4294static struct hda_amp_list vt1718S_loopbacks[] = {
4295 { 0x21, HDA_INPUT, 1 },
4296 { 0x21, HDA_INPUT, 2 },
4297 { 0x21, HDA_INPUT, 3 },
4298 { 0x21, HDA_INPUT, 4 },
4299 { } /* end */
4300};
4301#endif
4302
4303static int patch_vt1718S(struct hda_codec *codec)
4304{
4305 struct via_spec *spec;
4306 int err;
4307
4308 /* create a codec specific record */
4309 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4310 if (spec == NULL)
4311 return -ENOMEM;
4312
4313 codec->spec = spec;
4314
4315 /* automatic parse from the BIOS config */
4316 err = vt1718S_parse_auto_config(codec);
4317 if (err < 0) {
4318 via_free(codec);
4319 return err;
4320 } else if (!err) {
4321 printk(KERN_INFO "hda_codec: Cannot set up configuration "
4322 "from BIOS. Using genenic mode...\n");
4323 }
4324
4325 spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
4326 spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
4327
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08004328 if (codec->vendor_id == 0x11060441)
4329 spec->stream_name_analog = "VT2020 Analog";
4330 else if (codec->vendor_id == 0x11064441)
4331 spec->stream_name_analog = "VT1828S Analog";
4332 else
4333 spec->stream_name_analog = "VT1718S Analog";
Lydia Wangeb7188c2009-10-10 19:08:34 +08004334 spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
4335 spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
4336
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08004337 if (codec->vendor_id == 0x11060441)
4338 spec->stream_name_digital = "VT2020 Digital";
4339 else if (codec->vendor_id == 0x11064441)
4340 spec->stream_name_digital = "VT1828S Digital";
4341 else
4342 spec->stream_name_digital = "VT1718S Digital";
Lydia Wangeb7188c2009-10-10 19:08:34 +08004343 spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08004344 if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
Lydia Wangeb7188c2009-10-10 19:08:34 +08004345 spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
4346
4347 if (!spec->adc_nids && spec->input_mux) {
4348 spec->adc_nids = vt1718S_adc_nids;
4349 spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids);
4350 get_mux_nids(codec);
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08004351 override_mic_boost(codec, 0x2b, 0, 3, 40);
4352 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wangeb7188c2009-10-10 19:08:34 +08004353 spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
4354 spec->num_mixers++;
4355 }
4356
4357 codec->patch_ops = via_patch_ops;
4358
4359 codec->patch_ops.init = via_auto_init;
4360 codec->patch_ops.unsol_event = via_unsol_event,
4361
4362#ifdef CONFIG_SND_HDA_POWER_SAVE
4363 spec->loopback.amplist = vt1718S_loopbacks;
4364#endif
4365
4366 return 0;
4367}
Joseph Chanc577b8a2006-11-29 15:29:40 +01004368/*
4369 * patch entries
4370 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004371static struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01004372 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
4373 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
4374 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
4375 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
4376 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004377 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004378 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004379 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004380 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004381 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004382 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004383 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004384 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004385 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004386 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004387 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004388 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004389 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004390 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004391 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004392 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004393 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004394 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004395 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004396 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004397 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004398 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004399 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004400 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004401 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004402 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004403 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004404 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004405 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004406 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01004407 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01004408 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08004409 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01004410 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08004411 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01004412 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08004413 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01004414 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08004415 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01004416 { .id = 0x11064397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08004417 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01004418 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08004419 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01004420 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08004421 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01004422 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08004423 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01004424 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08004425 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01004426 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08004427 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01004428 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08004429 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01004430 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08004431 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01004432 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08004433 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01004434 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08004435 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01004436 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08004437 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01004438 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08004439 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08004440 { .id = 0x11060428, .name = "VT1718S",
4441 .patch = patch_vt1718S},
4442 { .id = 0x11064428, .name = "VT1718S",
4443 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08004444 { .id = 0x11060441, .name = "VT2020",
4445 .patch = patch_vt1718S},
4446 { .id = 0x11064441, .name = "VT1828S",
4447 .patch = patch_vt1718S},
Joseph Chanc577b8a2006-11-29 15:29:40 +01004448 {} /* terminator */
4449};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004450
4451MODULE_ALIAS("snd-hda-codec-id:1106*");
4452
4453static struct hda_codec_preset_list via_list = {
4454 .preset = snd_hda_preset_via,
4455 .owner = THIS_MODULE,
4456};
4457
4458MODULE_LICENSE("GPL");
4459MODULE_DESCRIPTION("VIA HD-audio codec");
4460
4461static int __init patch_via_init(void)
4462{
4463 return snd_hda_add_codec_preset(&via_list);
4464}
4465
4466static void __exit patch_via_exit(void)
4467{
4468 snd_hda_delete_codec_preset(&via_list);
4469}
4470
4471module_init(patch_via_init)
4472module_exit(patch_via_exit)