blob: 8ec2db2e9b6016700cbc5d9852c4d015d9650e32 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
4 * HD audio interface patch for ALC 260/880/882 codecs
5 *
Kailang Yangdf694da2005-12-05 19:42:22 +01006 * Copyright (c) 2004 Kailang Yang <kailang@realtek.com.tw>
7 * PeiSen Hou <pshou@realtek.com.tw>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * Takashi Iwai <tiwai@suse.de>
9 *
10 * This driver is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This driver is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25#include <sound/driver.h>
26#include <linux/init.h>
27#include <linux/delay.h>
28#include <linux/slab.h>
29#include <linux/pci.h>
30#include <sound/core.h>
31#include "hda_codec.h"
32#include "hda_local.h"
33
34
35/* ALC880 board config type */
36enum {
Linus Torvalds1da177e2005-04-16 15:20:36 -070037 ALC880_3ST,
38 ALC880_3ST_DIG,
39 ALC880_5ST,
40 ALC880_5ST_DIG,
41 ALC880_W810,
Takashi Iwaidfc0ff62005-05-12 14:31:49 +020042 ALC880_Z71V,
Takashi Iwaib6482d42005-06-27 15:32:43 +020043 ALC880_6ST,
Takashi Iwai16ded522005-06-10 19:58:24 +020044 ALC880_6ST_DIG,
45 ALC880_F1734,
46 ALC880_ASUS,
47 ALC880_ASUS_DIG,
48 ALC880_ASUS_W1V,
Kailang Yangdf694da2005-12-05 19:42:22 +010049 ALC880_ASUS_DIG2,
Takashi Iwai16ded522005-06-10 19:58:24 +020050 ALC880_UNIWILL_DIG,
Kailang Yangdf694da2005-12-05 19:42:22 +010051 ALC880_CLEVO,
52 ALC880_TCL_S700,
Takashi Iwaie9edcee2005-06-13 14:16:38 +020053#ifdef CONFIG_SND_DEBUG
54 ALC880_TEST,
55#endif
Kailang Yangdf694da2005-12-05 19:42:22 +010056 ALC880_AUTO,
Takashi Iwai16ded522005-06-10 19:58:24 +020057 ALC880_MODEL_LAST /* last tag */
58};
59
60/* ALC260 models */
61enum {
62 ALC260_BASIC,
63 ALC260_HP,
Kailang Yangdf694da2005-12-05 19:42:22 +010064 ALC260_HP_3013,
65 ALC260_FUJITSU_S702X,
66 ALC260_AUTO,
Takashi Iwai16ded522005-06-10 19:58:24 +020067 ALC260_MODEL_LAST /* last tag */
Linus Torvalds1da177e2005-04-16 15:20:36 -070068};
69
Kailang Yangdf694da2005-12-05 19:42:22 +010070/* ALC262 models */
71enum {
72 ALC262_BASIC,
73 ALC262_AUTO,
74 ALC262_MODEL_LAST /* last tag */
75};
76
77/* ALC861 models */
78enum {
79 ALC861_3ST,
80 ALC861_3ST_DIG,
81 ALC861_6ST_DIG,
82 ALC861_AUTO,
83 ALC861_MODEL_LAST,
84};
85
86/* ALC882 models */
87enum {
88 ALC882_3ST_DIG,
89 ALC882_6ST_DIG,
90 ALC882_AUTO,
91 ALC882_MODEL_LAST,
92};
93
94/* for GPIO Poll */
95#define GPIO_MASK 0x03
96
Linus Torvalds1da177e2005-04-16 15:20:36 -070097struct alc_spec {
98 /* codec parameterization */
Kailang Yangdf694da2005-12-05 19:42:22 +010099 struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 unsigned int num_mixers;
101
Kailang Yangdf694da2005-12-05 19:42:22 +0100102 const struct hda_verb *init_verbs[5]; /* initialization verbs
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200103 * don't forget NULL termination!
104 */
105 unsigned int num_init_verbs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106
Takashi Iwai16ded522005-06-10 19:58:24 +0200107 char *stream_name_analog; /* analog PCM stream */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 struct hda_pcm_stream *stream_analog_playback;
109 struct hda_pcm_stream *stream_analog_capture;
110
Takashi Iwai16ded522005-06-10 19:58:24 +0200111 char *stream_name_digital; /* digital PCM stream */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 struct hda_pcm_stream *stream_digital_playback;
113 struct hda_pcm_stream *stream_digital_capture;
114
115 /* playback */
Takashi Iwai16ded522005-06-10 19:58:24 +0200116 struct hda_multi_out multiout; /* playback set-up
117 * max_channels, dacs must be set
118 * dig_out_nid and hp_nid are optional
119 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
121 /* capture */
122 unsigned int num_adc_nids;
123 hda_nid_t *adc_nids;
Takashi Iwai16ded522005-06-10 19:58:24 +0200124 hda_nid_t dig_in_nid; /* digital-in NID; optional */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
126 /* capture source */
127 const struct hda_input_mux *input_mux;
128 unsigned int cur_mux[3];
129
130 /* channel model */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100131 const struct hda_channel_mode *channel_mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 int num_channel_mode;
133
134 /* PCM information */
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100135 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
Takashi Iwai41e41f12005-06-08 14:48:49 +0200136
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200137 /* dynamic controls, init_verbs and input_mux */
138 struct auto_pin_cfg autocfg;
139 unsigned int num_kctl_alloc, num_kctl_used;
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100140 struct snd_kcontrol_new *kctl_alloc;
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200141 struct hda_input_mux private_imux;
Kailang Yangdf694da2005-12-05 19:42:22 +0100142 hda_nid_t private_dac_nids[5];
143};
144
145/*
146 * configuration template - to be copied to the spec instance
147 */
148struct alc_config_preset {
149 struct snd_kcontrol_new *mixers[5]; /* should be identical size with spec */
150 const struct hda_verb *init_verbs[5];
151 unsigned int num_dacs;
152 hda_nid_t *dac_nids;
153 hda_nid_t dig_out_nid; /* optional */
154 hda_nid_t hp_nid; /* optional */
155 unsigned int num_adc_nids;
156 hda_nid_t *adc_nids;
157 hda_nid_t dig_in_nid;
158 unsigned int num_channel_mode;
159 const struct hda_channel_mode *channel_mode;
160 const struct hda_input_mux *input_mux;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161};
162
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163
164/*
165 * input MUX handling
166 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100167static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168{
169 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
170 struct alc_spec *spec = codec->spec;
171 return snd_hda_input_mux_info(spec->input_mux, uinfo);
172}
173
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100174static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175{
176 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
177 struct alc_spec *spec = codec->spec;
178 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
179
180 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
181 return 0;
182}
183
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100184static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185{
186 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
187 struct alc_spec *spec = codec->spec;
188 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
189 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
190 spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);
191}
192
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200193
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194/*
195 * channel mode setting
196 */
Kailang Yangdf694da2005-12-05 19:42:22 +0100197static int alc_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198{
199 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
200 struct alc_spec *spec = codec->spec;
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100201 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
202 spec->num_channel_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203}
204
Kailang Yangdf694da2005-12-05 19:42:22 +0100205static int alc_ch_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206{
207 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
208 struct alc_spec *spec = codec->spec;
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100209 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
210 spec->num_channel_mode, spec->multiout.max_channels);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211}
212
Kailang Yangdf694da2005-12-05 19:42:22 +0100213static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214{
215 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
216 struct alc_spec *spec = codec->spec;
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100217 return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
218 spec->num_channel_mode, &spec->multiout.max_channels);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219}
220
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221/*
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100222 * Control the mode of pin widget settings via the mixer. "pc" is used
223 * instead of "%" to avoid consequences of accidently treating the % as
224 * being part of a format specifier. Maximum allowed length of a value is
225 * 63 characters plus NULL terminator.
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200226 */
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100227static char *alc_pin_mode_names[] = {
228 "Line in", "Mic 80pc bias", "Mic 50pc bias",
229 "Line out", "Headphone out",
230};
231static unsigned char alc_pin_mode_values[] = {
232 PIN_IN, PIN_VREF80, PIN_VREF50, PIN_OUT, PIN_HP,
233};
234/* The control can present all 5 options, or it can limit the options based
235 * in the pin being assumed to be exclusively an input or an output pin.
236 */
237#define ALC_PIN_DIR_IN 0x00
238#define ALC_PIN_DIR_OUT 0x01
239#define ALC_PIN_DIR_INOUT 0x02
240
241/* Info about the pin modes supported by the three different pin directions.
242 * For each direction the minimum and maximum values are given.
243 */
244static signed char alc_pin_mode_dir_info[3][2] = {
245 { 0, 2 }, /* ALC_PIN_DIR_IN */
246 { 3, 4 }, /* ALC_PIN_DIR_OUT */
247 { 0, 4 }, /* ALC_PIN_DIR_INOUT */
248};
249#define alc_pin_mode_min(_dir) (alc_pin_mode_dir_info[_dir][0])
250#define alc_pin_mode_max(_dir) (alc_pin_mode_dir_info[_dir][1])
251#define alc_pin_mode_n_items(_dir) \
252 (alc_pin_mode_max(_dir)-alc_pin_mode_min(_dir)+1)
253
254static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200255{
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100256 unsigned int item_num = uinfo->value.enumerated.item;
257 unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
258
259 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200260 uinfo->count = 1;
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100261 uinfo->value.enumerated.items = alc_pin_mode_n_items(dir);
262
263 if (item_num<alc_pin_mode_min(dir) || item_num>alc_pin_mode_max(dir))
264 item_num = alc_pin_mode_min(dir);
265 strcpy(uinfo->value.enumerated.name, alc_pin_mode_names[item_num]);
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200266 return 0;
267}
268
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100269static int alc_pin_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200270{
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100271 unsigned int i;
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200272 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
273 hda_nid_t nid = kcontrol->private_value & 0xffff;
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100274 unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200275 long *valp = ucontrol->value.integer.value;
276 unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00);
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200277
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100278 /* Find enumerated value for current pinctl setting */
279 i = alc_pin_mode_min(dir);
280 while (alc_pin_mode_values[i]!=pinctl && i<=alc_pin_mode_max(dir))
281 i++;
282 *valp = i<=alc_pin_mode_max(dir)?i:alc_pin_mode_min(dir);
283 return 0;
284}
285
286static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
287{
288 signed int change;
289 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
290 hda_nid_t nid = kcontrol->private_value & 0xffff;
291 unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
292 long val = *ucontrol->value.integer.value;
293 unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00);
294
295 if (val<alc_pin_mode_min(dir) || val>alc_pin_mode_max(dir))
296 val = alc_pin_mode_min(dir);
297
298 change = pinctl != alc_pin_mode_values[val];
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200299 if (change)
300 snd_hda_codec_write(codec,nid,0,AC_VERB_SET_PIN_WIDGET_CONTROL,
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100301 alc_pin_mode_values[val]);
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200302 return change;
303}
304
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100305#define ALC_PIN_MODE(xname, nid, dir) \
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200306 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100307 .info = alc_pin_mode_info, \
308 .get = alc_pin_mode_get, \
309 .put = alc_pin_mode_put, \
310 .private_value = nid | (dir<<16) }
Kailang Yangdf694da2005-12-05 19:42:22 +0100311
312/*
313 * set up from the preset table
314 */
315static void setup_preset(struct alc_spec *spec, const struct alc_config_preset *preset)
316{
317 int i;
318
319 for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
320 spec->mixers[spec->num_mixers++] = preset->mixers[i];
321 for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; i++)
322 spec->init_verbs[spec->num_init_verbs++] = preset->init_verbs[i];
323
324 spec->channel_mode = preset->channel_mode;
325 spec->num_channel_mode = preset->num_channel_mode;
326
327 spec->multiout.max_channels = spec->channel_mode[0].channels;
328
329 spec->multiout.num_dacs = preset->num_dacs;
330 spec->multiout.dac_nids = preset->dac_nids;
331 spec->multiout.dig_out_nid = preset->dig_out_nid;
332 spec->multiout.hp_nid = preset->hp_nid;
333
334 spec->input_mux = preset->input_mux;
335
336 spec->num_adc_nids = preset->num_adc_nids;
337 spec->adc_nids = preset->adc_nids;
338 spec->dig_in_nid = preset->dig_in_nid;
339}
340
Takashi Iwai41e41f12005-06-08 14:48:49 +0200341/*
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200342 * ALC880 3-stack model
343 *
344 * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e)
345 * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18, F-Mic = 0x1b
346 * HP = 0x19
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 */
348
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200349static hda_nid_t alc880_dac_nids[4] = {
350 /* front, rear, clfe, rear_surr */
351 0x02, 0x05, 0x04, 0x03
352};
353
354static hda_nid_t alc880_adc_nids[3] = {
355 /* ADC0-2 */
356 0x07, 0x08, 0x09,
357};
358
359/* The datasheet says the node 0x07 is connected from inputs,
360 * but it shows zero connection in the real implementation on some devices.
Kailang Yangdf694da2005-12-05 19:42:22 +0100361 * Note: this is a 915GAV bug, fixed on 915GLV
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 */
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200363static hda_nid_t alc880_adc_nids_alt[2] = {
364 /* ADC1-2 */
365 0x08, 0x09,
366};
367
368#define ALC880_DIGOUT_NID 0x06
369#define ALC880_DIGIN_NID 0x0a
370
371static struct hda_input_mux alc880_capture_source = {
372 .num_items = 4,
373 .items = {
374 { "Mic", 0x0 },
375 { "Front Mic", 0x3 },
376 { "Line", 0x2 },
377 { "CD", 0x4 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 },
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200379};
380
381/* channel source setting (2/6 channel selection for 3-stack) */
382/* 2ch mode */
383static struct hda_verb alc880_threestack_ch2_init[] = {
384 /* set line-in to input, mute it */
385 { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
386 { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
387 /* set mic-in to input vref 80%, mute it */
388 { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
389 { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 { } /* end */
391};
392
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200393/* 6ch mode */
394static struct hda_verb alc880_threestack_ch6_init[] = {
395 /* set line-in to output, unmute it */
396 { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
397 { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
398 /* set mic-in to output, unmute it */
399 { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
400 { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
401 { } /* end */
402};
403
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100404static struct hda_channel_mode alc880_threestack_modes[2] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200405 { 2, alc880_threestack_ch2_init },
406 { 6, alc880_threestack_ch6_init },
407};
408
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100409static struct snd_kcontrol_new alc880_three_stack_mixer[] = {
Takashi Iwai05acb862005-06-10 19:50:25 +0200410 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100411 HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
Takashi Iwai05acb862005-06-10 19:50:25 +0200412 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100413 HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT),
Takashi Iwai05acb862005-06-10 19:50:25 +0200414 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
415 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100416 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
417 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
419 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
420 HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
421 HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
422 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
423 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
424 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
425 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT),
426 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
427 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT),
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200429 {
430 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
431 .name = "Channel Mode",
Kailang Yangdf694da2005-12-05 19:42:22 +0100432 .info = alc_ch_mode_info,
433 .get = alc_ch_mode_get,
434 .put = alc_ch_mode_put,
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200435 },
436 { } /* end */
437};
438
439/* capture mixer elements */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100440static struct snd_kcontrol_new alc880_capture_mixer[] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200441 HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
442 HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
443 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
444 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
445 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
446 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
447 {
448 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
449 /* The multiple "Capture Source" controls confuse alsamixer
450 * So call somewhat different..
451 * FIXME: the controls appear in the "playback" view!
452 */
453 /* .name = "Capture Source", */
454 .name = "Input Source",
455 .count = 3,
456 .info = alc_mux_enum_info,
457 .get = alc_mux_enum_get,
458 .put = alc_mux_enum_put,
459 },
460 { } /* end */
461};
462
463/* capture mixer elements (in case NID 0x07 not available) */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100464static struct snd_kcontrol_new alc880_capture_alt_mixer[] = {
Takashi Iwai71fe7b82005-05-25 18:11:40 +0200465 HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
466 HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
467 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
468 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 {
470 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
471 /* The multiple "Capture Source" controls confuse alsamixer
472 * So call somewhat different..
473 * FIXME: the controls appear in the "playback" view!
474 */
475 /* .name = "Capture Source", */
476 .name = "Input Source",
477 .count = 2,
478 .info = alc_mux_enum_info,
479 .get = alc_mux_enum_get,
480 .put = alc_mux_enum_put,
481 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 { } /* end */
483};
484
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200485
486
487/*
488 * ALC880 5-stack model
489 *
490 * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d), Side = 0x02 (0xd)
491 * Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16
492 * Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19
493 */
494
495/* additional mixers to alc880_three_stack_mixer */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100496static struct snd_kcontrol_new alc880_five_stack_mixer[] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200497 HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100498 HDA_BIND_MUTE("Side Playback Switch", 0x0d, 2, HDA_INPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 { } /* end */
500};
501
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200502/* channel source setting (6/8 channel selection for 5-stack) */
503/* 6ch mode */
504static struct hda_verb alc880_fivestack_ch6_init[] = {
505 /* set line-in to input, mute it */
506 { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
507 { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
Takashi Iwaidfc0ff62005-05-12 14:31:49 +0200508 { } /* end */
509};
510
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200511/* 8ch mode */
512static struct hda_verb alc880_fivestack_ch8_init[] = {
513 /* set line-in to output, unmute it */
514 { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
515 { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
516 { } /* end */
517};
518
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100519static struct hda_channel_mode alc880_fivestack_modes[2] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200520 { 6, alc880_fivestack_ch6_init },
521 { 8, alc880_fivestack_ch8_init },
522};
523
524
525/*
526 * ALC880 6-stack model
527 *
528 * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e), Side = 0x05 (0x0f)
529 * Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17,
530 * Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b
531 */
532
533static hda_nid_t alc880_6st_dac_nids[4] = {
534 /* front, rear, clfe, rear_surr */
535 0x02, 0x03, 0x04, 0x05
536};
537
538static struct hda_input_mux alc880_6stack_capture_source = {
539 .num_items = 4,
540 .items = {
541 { "Mic", 0x0 },
542 { "Front Mic", 0x1 },
543 { "Line", 0x2 },
544 { "CD", 0x4 },
545 },
546};
547
548/* fixed 8-channels */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100549static struct hda_channel_mode alc880_sixstack_modes[1] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200550 { 8, NULL },
551};
552
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100553static struct snd_kcontrol_new alc880_six_stack_mixer[] = {
Takashi Iwai16ded522005-06-10 19:58:24 +0200554 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100555 HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200556 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100557 HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200558 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
559 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100560 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
561 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200562 HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100563 HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200564 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
565 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
566 HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
567 HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
568 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
569 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
570 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
571 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
572 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
573 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200574 {
575 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
576 .name = "Channel Mode",
Kailang Yangdf694da2005-12-05 19:42:22 +0100577 .info = alc_ch_mode_info,
578 .get = alc_ch_mode_get,
579 .put = alc_ch_mode_put,
Takashi Iwai16ded522005-06-10 19:58:24 +0200580 },
581 { } /* end */
582};
583
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200584
585/*
586 * ALC880 W810 model
587 *
588 * W810 has rear IO for:
589 * Front (DAC 02)
590 * Surround (DAC 03)
591 * Center/LFE (DAC 04)
592 * Digital out (06)
593 *
594 * The system also has a pair of internal speakers, and a headphone jack.
595 * These are both connected to Line2 on the codec, hence to DAC 02.
596 *
597 * There is a variable resistor to control the speaker or headphone
598 * volume. This is a hardware-only device without a software API.
599 *
600 * Plugging headphones in will disable the internal speakers. This is
601 * implemented in hardware, not via the driver using jack sense. In
602 * a similar fashion, plugging into the rear socket marked "front" will
603 * disable both the speakers and headphones.
604 *
605 * For input, there's a microphone jack, and an "audio in" jack.
606 * These may not do anything useful with this driver yet, because I
607 * haven't setup any initialization verbs for these yet...
608 */
609
610static hda_nid_t alc880_w810_dac_nids[3] = {
611 /* front, rear/surround, clfe */
612 0x02, 0x03, 0x04
613};
614
615/* fixed 6 channels */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100616static struct hda_channel_mode alc880_w810_modes[1] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200617 { 6, NULL }
618};
619
620/* Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, HP = 0x1b */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100621static struct snd_kcontrol_new alc880_w810_base_mixer[] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200622 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100623 HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200624 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100625 HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200626 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
627 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100628 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
629 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200630 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
631 { } /* end */
632};
633
634
635/*
636 * Z710V model
637 *
638 * DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d)
639 * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?), Line = 0x1a
640 */
641
642static hda_nid_t alc880_z71v_dac_nids[1] = {
643 0x02
644};
645#define ALC880_Z71V_HP_DAC 0x03
646
647/* fixed 2 channels */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100648static struct hda_channel_mode alc880_2_jack_modes[1] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200649 { 2, NULL }
650};
651
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100652static struct snd_kcontrol_new alc880_z71v_mixer[] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200653 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100654 HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200655 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100656 HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT),
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200657 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
658 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
659 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
660 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
661 { } /* end */
662};
663
664
665/* FIXME! */
666/*
667 * ALC880 F1734 model
668 *
669 * DAC: HP = 0x02 (0x0c), Front = 0x03 (0x0d)
670 * Pin assignment: HP = 0x14, Front = 0x15, Mic = 0x18
671 */
672
673static hda_nid_t alc880_f1734_dac_nids[1] = {
674 0x03
675};
676#define ALC880_F1734_HP_DAC 0x02
677
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100678static struct snd_kcontrol_new alc880_f1734_mixer[] = {
Takashi Iwai16ded522005-06-10 19:58:24 +0200679 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100680 HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200681 HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100682 HDA_BIND_MUTE("Internal Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200683 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
684 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
685 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
686 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200687 { } /* end */
688};
689
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200690
691/* FIXME! */
692/*
693 * ALC880 ASUS model
694 *
695 * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e)
696 * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16,
697 * Mic = 0x18, Line = 0x1a
698 */
699
700#define alc880_asus_dac_nids alc880_w810_dac_nids /* identical with w810 */
701#define alc880_asus_modes alc880_threestack_modes /* 2/6 channel mode */
702
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100703static struct snd_kcontrol_new alc880_asus_mixer[] = {
Takashi Iwai16ded522005-06-10 19:58:24 +0200704 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100705 HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200706 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100707 HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200708 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
709 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100710 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
711 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200712 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
713 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
714 HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
715 HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
716 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
717 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200718 {
719 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
720 .name = "Channel Mode",
Kailang Yangdf694da2005-12-05 19:42:22 +0100721 .info = alc_ch_mode_info,
722 .get = alc_ch_mode_get,
723 .put = alc_ch_mode_put,
Takashi Iwai16ded522005-06-10 19:58:24 +0200724 },
725 { } /* end */
726};
727
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200728/* FIXME! */
729/*
730 * ALC880 ASUS W1V model
731 *
732 * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e)
733 * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16,
734 * Mic = 0x18, Line = 0x1a, Line2 = 0x1b
735 */
736
737/* additional mixers to alc880_asus_mixer */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100738static struct snd_kcontrol_new alc880_asus_w1v_mixer[] = {
Takashi Iwai16ded522005-06-10 19:58:24 +0200739 HDA_CODEC_VOLUME("Line2 Playback Volume", 0x0b, 0x03, HDA_INPUT),
740 HDA_CODEC_MUTE("Line2 Playback Switch", 0x0b, 0x03, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200741 { } /* end */
742};
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200743
Takashi Iwai3c10a9d2005-08-23 20:02:27 +0200744/* additional mixers to alc880_asus_mixer */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100745static struct snd_kcontrol_new alc880_pcbeep_mixer[] = {
Takashi Iwai3c10a9d2005-08-23 20:02:27 +0200746 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
747 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
748 { } /* end */
749};
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200750
Kailang Yangdf694da2005-12-05 19:42:22 +0100751/* TCL S700 */
752static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
753 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
754 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
755 HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
756 HDA_CODEC_VOLUME("CD Playback Volume", 0x0B, 0x04, HDA_INPUT),
757 HDA_CODEC_MUTE("CD Playback Switch", 0x0B, 0x04, HDA_INPUT),
758 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0B, 0x0, HDA_INPUT),
759 HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT),
760 HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
761 HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
762 {
763 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
764 /* The multiple "Capture Source" controls confuse alsamixer
765 * So call somewhat different..
766 * FIXME: the controls appear in the "playback" view!
767 */
768 /* .name = "Capture Source", */
769 .name = "Input Source",
770 .count = 1,
771 .info = alc_mux_enum_info,
772 .get = alc_mux_enum_get,
773 .put = alc_mux_enum_put,
774 },
775 { } /* end */
776};
777
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778/*
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200779 * build control elements
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 */
781static int alc_build_controls(struct hda_codec *codec)
782{
783 struct alc_spec *spec = codec->spec;
784 int err;
785 int i;
786
787 for (i = 0; i < spec->num_mixers; i++) {
788 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
789 if (err < 0)
790 return err;
791 }
792
793 if (spec->multiout.dig_out_nid) {
794 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
795 if (err < 0)
796 return err;
797 }
798 if (spec->dig_in_nid) {
799 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
800 if (err < 0)
801 return err;
802 }
803 return 0;
804}
805
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200806
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807/*
808 * initialize the codec volumes, etc
809 */
810
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200811/*
812 * generic initialization of ADC, input mixers and output mixers
813 */
814static struct hda_verb alc880_volume_init_verbs[] = {
815 /*
816 * Unmute ADC0-2 and set the default input to mic-in
817 */
Takashi Iwai71fe7b82005-05-25 18:11:40 +0200818 {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200819 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +0200820 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200821 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +0200822 {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200823 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200825 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
826 * mixer widget
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 * Note: PASD motherboards uses the Line In 2 as the input for front panel
828 * mic (mic 2)
829 */
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200830 /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
Takashi Iwai16ded522005-06-10 19:58:24 +0200831 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200832 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
833 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
834 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
835 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200837 /*
838 * Set up output mixers (0x0c - 0x0f)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 */
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200840 /* set vol=0 to output mixers */
841 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
842 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
843 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
844 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
845 /* set up input amps for analog loopback */
846 /* Amp Indices: DAC = 0, mixer = 1 */
Takashi Iwai05acb862005-06-10 19:50:25 +0200847 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
848 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwai05acb862005-06-10 19:50:25 +0200849 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
850 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwai05acb862005-06-10 19:50:25 +0200851 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
852 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwai05acb862005-06-10 19:50:25 +0200853 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
854 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855
856 { }
857};
858
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200859/*
860 * 3-stack pin configuration:
861 * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b
862 */
863static struct hda_verb alc880_pin_3stack_init_verbs[] = {
864 /*
865 * preset connection lists of input pins
866 * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround
867 */
868 {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
869 {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
870 {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */
871
872 /*
873 * Set pin mode and muting
874 */
875 /* set front pin widgets 0x14 for output */
876 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
877 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
878 /* Mic1 (rear panel) pin widget for input and vref at 80% */
879 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
880 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
881 /* Mic2 (as headphone out) for HP output */
882 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
883 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
884 /* Line In pin widget for input */
885 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
886 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
887 /* Line2 (as front mic) pin widget for input and vref at 80% */
888 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
889 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
890 /* CD pin widget for input */
891 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
892
893 { }
894};
895
896/*
897 * 5-stack pin configuration:
898 * front = 0x14, surround = 0x17, clfe = 0x16, mic = 0x18, HP = 0x19,
899 * line-in/side = 0x1a, f-mic = 0x1b
900 */
901static struct hda_verb alc880_pin_5stack_init_verbs[] = {
902 /*
903 * preset connection lists of input pins
904 * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround
905 */
906 {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
907 {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/side */
908
909 /*
910 * Set pin mode and muting
911 */
912 /* set pin widgets 0x14-0x17 for output */
Takashi Iwai05acb862005-06-10 19:50:25 +0200913 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
914 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
915 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
916 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200917 /* unmute pins for output (no gain on this amp) */
918 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
919 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
920 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
921 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
922
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 /* Mic1 (rear panel) pin widget for input and vref at 80% */
Takashi Iwai16ded522005-06-10 19:58:24 +0200924 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200925 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
926 /* Mic2 (as headphone out) for HP output */
927 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Takashi Iwai05acb862005-06-10 19:50:25 +0200928 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200929 /* Line In pin widget for input */
930 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
931 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
932 /* Line2 (as front mic) pin widget for input and vref at 80% */
933 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
934 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
935 /* CD pin widget for input */
936 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
938 { }
939};
940
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200941/*
942 * W810 pin configuration:
943 * front = 0x14, surround = 0x15, clfe = 0x16, HP = 0x1b
944 */
945static struct hda_verb alc880_pin_w810_init_verbs[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 /* hphone/speaker input selector: front DAC */
947 {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
948
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200949 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
950 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
951 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
952 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
953 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
954 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
955
956 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Takashi Iwai05acb862005-06-10 19:50:25 +0200957 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 { }
960};
961
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200962/*
963 * Z71V pin configuration:
964 * Speaker-out = 0x14, HP = 0x15, Mic = 0x18, Line-in = 0x1a, Mic2 = 0x1b (?)
965 */
966static struct hda_verb alc880_pin_z71v_init_verbs[] = {
Takashi Iwai05acb862005-06-10 19:50:25 +0200967 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200968 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwai05acb862005-06-10 19:50:25 +0200969 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200970 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaidfc0ff62005-05-12 14:31:49 +0200971
Takashi Iwai16ded522005-06-10 19:58:24 +0200972 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200973 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai16ded522005-06-10 19:58:24 +0200974 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200975 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidfc0ff62005-05-12 14:31:49 +0200976
977 { }
978};
979
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200980/*
981 * 6-stack pin configuration:
982 * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18, f-mic = 0x19,
983 * line = 0x1a, HP = 0x1b
984 */
985static struct hda_verb alc880_pin_6stack_init_verbs[] = {
986 {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
987
Takashi Iwai16ded522005-06-10 19:58:24 +0200988 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200989 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwai16ded522005-06-10 19:58:24 +0200990 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200991 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwai16ded522005-06-10 19:58:24 +0200992 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200993 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwai16ded522005-06-10 19:58:24 +0200994 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200995 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
996
Takashi Iwai16ded522005-06-10 19:58:24 +0200997 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200998 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Takashi Iwai16ded522005-06-10 19:58:24 +0200999 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001000 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Takashi Iwai16ded522005-06-10 19:58:24 +02001001 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001002 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Takashi Iwai16ded522005-06-10 19:58:24 +02001003 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Takashi Iwai16ded522005-06-10 19:58:24 +02001004 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwai16ded522005-06-10 19:58:24 +02001005 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1006
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001007 { }
1008};
Takashi Iwai16ded522005-06-10 19:58:24 +02001009
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001010/* FIXME! */
1011/*
1012 * F1734 pin configuration:
1013 * HP = 0x14, speaker-out = 0x15, mic = 0x18
1014 */
1015static struct hda_verb alc880_pin_f1734_init_verbs[] = {
1016 {0x10, AC_VERB_SET_CONNECT_SEL, 0x02},
1017 {0x11, AC_VERB_SET_CONNECT_SEL, 0x00},
1018 {0x12, AC_VERB_SET_CONNECT_SEL, 0x01},
1019 {0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
1020
1021 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
1022 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1023 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1024 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1025
1026 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1027 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1028 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1029 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1030 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1031 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1032 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1033 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1034 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai16ded522005-06-10 19:58:24 +02001035
1036 { }
1037};
1038
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001039/* FIXME! */
1040/*
1041 * ASUS pin configuration:
1042 * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a
1043 */
1044static struct hda_verb alc880_pin_asus_init_verbs[] = {
1045 {0x10, AC_VERB_SET_CONNECT_SEL, 0x02},
1046 {0x11, AC_VERB_SET_CONNECT_SEL, 0x00},
1047 {0x12, AC_VERB_SET_CONNECT_SEL, 0x01},
1048 {0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
1049
1050 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
1051 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1052 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1053 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1054 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1055 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1056 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1057 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1058
1059 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1060 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1061 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1062 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1063 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1064 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1065 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1066 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1067 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1068
1069 { }
1070};
1071
1072/* Enable GPIO mask and set output */
1073static struct hda_verb alc880_gpio1_init_verbs[] = {
1074 {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
1075 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
1076 {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
Kailang Yangdf694da2005-12-05 19:42:22 +01001077
1078 { }
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001079};
1080
1081/* Enable GPIO mask and set output */
1082static struct hda_verb alc880_gpio2_init_verbs[] = {
1083 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
1084 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
1085 {0x01, AC_VERB_SET_GPIO_DATA, 0x02},
Kailang Yangdf694da2005-12-05 19:42:22 +01001086
1087 { }
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001088};
1089
Kailang Yangdf694da2005-12-05 19:42:22 +01001090/* Clevo m520g init */
1091static struct hda_verb alc880_pin_clevo_init_verbs[] = {
1092 /* headphone output */
1093 {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
1094 /* line-out */
1095 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1096 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1097 /* Line-in */
1098 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1099 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1100 /* CD */
1101 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1102 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1103 /* Mic1 (rear panel) */
1104 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1105 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1106 /* Mic2 (front panel) */
1107 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1108 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1109 /* headphone */
1110 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
1111 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1112 /* change to EAPD mode */
1113 {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
1114 {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
1115
1116 { }
1117};
1118
1119static struct hda_verb alc880_pin_tcl_S700_init_verbs[] = {
1120 /* Headphone output */
1121 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
1122 /* Front output*/
1123 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1124 {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
1125
1126 /* Line In pin widget for input */
1127 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1128 /* CD pin widget for input */
1129 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1130 /* Mic1 (rear panel) pin widget for input and vref at 80% */
1131 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1132
1133 /* change to EAPD mode */
1134 {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
1135 {0x20, AC_VERB_SET_PROC_COEF, 0x3070},
1136
1137 { }
1138};
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001139
1140/*
1141 */
Takashi Iwai16ded522005-06-10 19:58:24 +02001142
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143static int alc_init(struct hda_codec *codec)
1144{
1145 struct alc_spec *spec = codec->spec;
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001146 unsigned int i;
1147
1148 for (i = 0; i < spec->num_init_verbs; i++)
1149 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 return 0;
1151}
1152
1153#ifdef CONFIG_PM
1154/*
1155 * resume
1156 */
1157static int alc_resume(struct hda_codec *codec)
1158{
1159 struct alc_spec *spec = codec->spec;
1160 int i;
1161
1162 alc_init(codec);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001163 for (i = 0; i < spec->num_mixers; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 snd_hda_resume_ctls(codec, spec->mixers[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 if (spec->multiout.dig_out_nid)
1166 snd_hda_resume_spdif_out(codec);
1167 if (spec->dig_in_nid)
1168 snd_hda_resume_spdif_in(codec);
1169
1170 return 0;
1171}
1172#endif
1173
1174/*
1175 * Analog playback callbacks
1176 */
1177static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo,
1178 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001179 struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180{
1181 struct alc_spec *spec = codec->spec;
1182 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
1183}
1184
1185static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
1186 struct hda_codec *codec,
1187 unsigned int stream_tag,
1188 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001189 struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190{
1191 struct alc_spec *spec = codec->spec;
1192 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1193 format, substream);
1194}
1195
1196static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1197 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001198 struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199{
1200 struct alc_spec *spec = codec->spec;
1201 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
1202}
1203
1204/*
1205 * Digital out
1206 */
1207static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1208 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001209 struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210{
1211 struct alc_spec *spec = codec->spec;
1212 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1213}
1214
1215static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1216 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001217 struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218{
1219 struct alc_spec *spec = codec->spec;
1220 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1221}
1222
1223/*
1224 * Analog capture
1225 */
1226static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1227 struct hda_codec *codec,
1228 unsigned int stream_tag,
1229 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001230 struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231{
1232 struct alc_spec *spec = codec->spec;
1233
1234 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1235 stream_tag, 0, format);
1236 return 0;
1237}
1238
1239static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1240 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001241 struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242{
1243 struct alc_spec *spec = codec->spec;
1244
1245 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
1246 return 0;
1247}
1248
1249
1250/*
1251 */
1252static struct hda_pcm_stream alc880_pcm_analog_playback = {
1253 .substreams = 1,
1254 .channels_min = 2,
1255 .channels_max = 8,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001256 /* NID is set in alc_build_pcms */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 .ops = {
1258 .open = alc880_playback_pcm_open,
1259 .prepare = alc880_playback_pcm_prepare,
1260 .cleanup = alc880_playback_pcm_cleanup
1261 },
1262};
1263
1264static struct hda_pcm_stream alc880_pcm_analog_capture = {
1265 .substreams = 2,
1266 .channels_min = 2,
1267 .channels_max = 2,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001268 /* NID is set in alc_build_pcms */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 .ops = {
1270 .prepare = alc880_capture_pcm_prepare,
1271 .cleanup = alc880_capture_pcm_cleanup
1272 },
1273};
1274
1275static struct hda_pcm_stream alc880_pcm_digital_playback = {
1276 .substreams = 1,
1277 .channels_min = 2,
1278 .channels_max = 2,
1279 /* NID is set in alc_build_pcms */
1280 .ops = {
1281 .open = alc880_dig_playback_pcm_open,
1282 .close = alc880_dig_playback_pcm_close
1283 },
1284};
1285
1286static struct hda_pcm_stream alc880_pcm_digital_capture = {
1287 .substreams = 1,
1288 .channels_min = 2,
1289 .channels_max = 2,
1290 /* NID is set in alc_build_pcms */
1291};
1292
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01001293/* Used by alc_build_pcms to flag that a PCM has no playback stream */
1294static struct hda_pcm_stream alc_pcm_null_playback = {
1295 .substreams = 0,
1296 .channels_min = 0,
1297 .channels_max = 0,
1298};
1299
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300static int alc_build_pcms(struct hda_codec *codec)
1301{
1302 struct alc_spec *spec = codec->spec;
1303 struct hda_pcm *info = spec->pcm_rec;
1304 int i;
1305
1306 codec->num_pcms = 1;
1307 codec->pcm_info = info;
1308
1309 info->name = spec->stream_name_analog;
Takashi Iwai4a471b72005-12-07 13:56:29 +01001310 if (spec->stream_analog_playback) {
1311 snd_assert(spec->multiout.dac_nids, return -EINVAL);
1312 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
1313 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
1314 }
1315 if (spec->stream_analog_capture) {
1316 snd_assert(spec->adc_nids, return -EINVAL);
1317 info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1318 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1319 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320
Takashi Iwai4a471b72005-12-07 13:56:29 +01001321 if (spec->channel_mode) {
1322 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
1323 for (i = 0; i < spec->num_channel_mode; i++) {
1324 if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
1325 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
1326 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 }
1328 }
1329
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01001330 /* If the use of more than one ADC is requested for the current
1331 * model, configure a second analog capture-only PCM.
1332 */
1333 if (spec->num_adc_nids > 1) {
1334 codec->num_pcms++;
1335 info++;
1336 info->name = spec->stream_name_analog;
1337 /* No playback stream for second PCM */
1338 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = alc_pcm_null_playback;
1339 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
1340 if (spec->stream_analog_capture) {
1341 snd_assert(spec->adc_nids, return -EINVAL);
1342 info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1343 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[1];
1344 }
1345 }
1346
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1348 codec->num_pcms++;
1349 info++;
1350 info->name = spec->stream_name_digital;
Takashi Iwai4a471b72005-12-07 13:56:29 +01001351 if (spec->multiout.dig_out_nid &&
1352 spec->stream_digital_playback) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback);
1354 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
1355 }
Takashi Iwai4a471b72005-12-07 13:56:29 +01001356 if (spec->dig_in_nid &&
1357 spec->stream_digital_capture) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture);
1359 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
1360 }
1361 }
1362
1363 return 0;
1364}
1365
1366static void alc_free(struct hda_codec *codec)
1367{
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001368 struct alc_spec *spec = codec->spec;
1369 unsigned int i;
1370
1371 if (! spec)
1372 return;
1373
1374 if (spec->kctl_alloc) {
1375 for (i = 0; i < spec->num_kctl_used; i++)
1376 kfree(spec->kctl_alloc[i].name);
1377 kfree(spec->kctl_alloc);
1378 }
1379 kfree(spec);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380}
1381
1382/*
1383 */
1384static struct hda_codec_ops alc_patch_ops = {
1385 .build_controls = alc_build_controls,
1386 .build_pcms = alc_build_pcms,
1387 .init = alc_init,
1388 .free = alc_free,
1389#ifdef CONFIG_PM
1390 .resume = alc_resume,
1391#endif
1392};
1393
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001394
1395/*
1396 * Test configuration for debugging
1397 *
1398 * Almost all inputs/outputs are enabled. I/O pins can be configured via
1399 * enum controls.
1400 */
1401#ifdef CONFIG_SND_DEBUG
1402static hda_nid_t alc880_test_dac_nids[4] = {
1403 0x02, 0x03, 0x04, 0x05
1404};
1405
1406static struct hda_input_mux alc880_test_capture_source = {
1407 .num_items = 5,
1408 .items = {
1409 { "In-1", 0x0 },
1410 { "In-2", 0x1 },
1411 { "In-3", 0x2 },
1412 { "In-4", 0x3 },
1413 { "CD", 0x4 },
1414 },
1415};
1416
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +01001417static struct hda_channel_mode alc880_test_modes[4] = {
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001418 { 2, NULL },
Takashi Iwaifd2c3262005-05-13 17:18:42 +02001419 { 4, NULL },
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001420 { 6, NULL },
Takashi Iwaifd2c3262005-05-13 17:18:42 +02001421 { 8, NULL },
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001422};
1423
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001424static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001425{
1426 static char *texts[] = {
1427 "N/A", "Line Out", "HP Out",
1428 "In Hi-Z", "In 50%", "In Grd", "In 80%", "In 100%"
1429 };
1430 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1431 uinfo->count = 1;
1432 uinfo->value.enumerated.items = 8;
1433 if (uinfo->value.enumerated.item >= 8)
1434 uinfo->value.enumerated.item = 7;
1435 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1436 return 0;
1437}
1438
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001439static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001440{
1441 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1442 hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
1443 unsigned int pin_ctl, item = 0;
1444
1445 pin_ctl = snd_hda_codec_read(codec, nid, 0,
1446 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1447 if (pin_ctl & AC_PINCTL_OUT_EN) {
1448 if (pin_ctl & AC_PINCTL_HP_EN)
1449 item = 2;
1450 else
1451 item = 1;
1452 } else if (pin_ctl & AC_PINCTL_IN_EN) {
1453 switch (pin_ctl & AC_PINCTL_VREFEN) {
1454 case AC_PINCTL_VREF_HIZ: item = 3; break;
1455 case AC_PINCTL_VREF_50: item = 4; break;
1456 case AC_PINCTL_VREF_GRD: item = 5; break;
1457 case AC_PINCTL_VREF_80: item = 6; break;
1458 case AC_PINCTL_VREF_100: item = 7; break;
1459 }
1460 }
1461 ucontrol->value.enumerated.item[0] = item;
1462 return 0;
1463}
1464
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001465static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001466{
1467 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1468 hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
1469 static unsigned int ctls[] = {
1470 0, AC_PINCTL_OUT_EN, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN,
1471 AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ,
1472 AC_PINCTL_IN_EN | AC_PINCTL_VREF_50,
1473 AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD,
1474 AC_PINCTL_IN_EN | AC_PINCTL_VREF_80,
1475 AC_PINCTL_IN_EN | AC_PINCTL_VREF_100,
1476 };
1477 unsigned int old_ctl, new_ctl;
1478
1479 old_ctl = snd_hda_codec_read(codec, nid, 0,
1480 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1481 new_ctl = ctls[ucontrol->value.enumerated.item[0]];
1482 if (old_ctl != new_ctl) {
1483 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl);
1484 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
1485 ucontrol->value.enumerated.item[0] >= 3 ? 0xb080 : 0xb000);
1486 return 1;
1487 }
1488 return 0;
1489}
1490
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001491static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001492{
1493 static char *texts[] = {
1494 "Front", "Surround", "CLFE", "Side"
1495 };
1496 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1497 uinfo->count = 1;
1498 uinfo->value.enumerated.items = 4;
1499 if (uinfo->value.enumerated.item >= 4)
1500 uinfo->value.enumerated.item = 3;
1501 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1502 return 0;
1503}
1504
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001505static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001506{
1507 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1508 hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
1509 unsigned int sel;
1510
1511 sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0);
1512 ucontrol->value.enumerated.item[0] = sel & 3;
1513 return 0;
1514}
1515
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001516static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001517{
1518 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1519 hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
1520 unsigned int sel;
1521
1522 sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0) & 3;
1523 if (ucontrol->value.enumerated.item[0] != sel) {
1524 sel = ucontrol->value.enumerated.item[0] & 3;
1525 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, sel);
1526 return 1;
1527 }
1528 return 0;
1529}
1530
1531#define PIN_CTL_TEST(xname,nid) { \
1532 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
1533 .name = xname, \
1534 .info = alc_test_pin_ctl_info, \
1535 .get = alc_test_pin_ctl_get, \
1536 .put = alc_test_pin_ctl_put, \
1537 .private_value = nid \
1538 }
1539
1540#define PIN_SRC_TEST(xname,nid) { \
1541 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
1542 .name = xname, \
1543 .info = alc_test_pin_src_info, \
1544 .get = alc_test_pin_src_get, \
1545 .put = alc_test_pin_src_put, \
1546 .private_value = nid \
1547 }
1548
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001549static struct snd_kcontrol_new alc880_test_mixer[] = {
Takashi Iwai05acb862005-06-10 19:50:25 +02001550 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
1551 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
1552 HDA_CODEC_VOLUME("CLFE Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
1553 HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01001554 HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
1555 HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
1556 HDA_BIND_MUTE("CLFE Playback Switch", 0x0e, 2, HDA_INPUT),
1557 HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001558 PIN_CTL_TEST("Front Pin Mode", 0x14),
1559 PIN_CTL_TEST("Surround Pin Mode", 0x15),
1560 PIN_CTL_TEST("CLFE Pin Mode", 0x16),
1561 PIN_CTL_TEST("Side Pin Mode", 0x17),
1562 PIN_CTL_TEST("In-1 Pin Mode", 0x18),
1563 PIN_CTL_TEST("In-2 Pin Mode", 0x19),
1564 PIN_CTL_TEST("In-3 Pin Mode", 0x1a),
1565 PIN_CTL_TEST("In-4 Pin Mode", 0x1b),
1566 PIN_SRC_TEST("In-1 Pin Source", 0x18),
1567 PIN_SRC_TEST("In-2 Pin Source", 0x19),
1568 PIN_SRC_TEST("In-3 Pin Source", 0x1a),
1569 PIN_SRC_TEST("In-4 Pin Source", 0x1b),
1570 HDA_CODEC_VOLUME("In-1 Playback Volume", 0x0b, 0x0, HDA_INPUT),
1571 HDA_CODEC_MUTE("In-1 Playback Switch", 0x0b, 0x0, HDA_INPUT),
1572 HDA_CODEC_VOLUME("In-2 Playback Volume", 0x0b, 0x1, HDA_INPUT),
1573 HDA_CODEC_MUTE("In-2 Playback Switch", 0x0b, 0x1, HDA_INPUT),
1574 HDA_CODEC_VOLUME("In-3 Playback Volume", 0x0b, 0x2, HDA_INPUT),
1575 HDA_CODEC_MUTE("In-3 Playback Switch", 0x0b, 0x2, HDA_INPUT),
1576 HDA_CODEC_VOLUME("In-4 Playback Volume", 0x0b, 0x3, HDA_INPUT),
1577 HDA_CODEC_MUTE("In-4 Playback Switch", 0x0b, 0x3, HDA_INPUT),
1578 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x4, HDA_INPUT),
1579 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x4, HDA_INPUT),
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001580 {
1581 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1582 .name = "Channel Mode",
Kailang Yangdf694da2005-12-05 19:42:22 +01001583 .info = alc_ch_mode_info,
1584 .get = alc_ch_mode_get,
1585 .put = alc_ch_mode_put,
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001586 },
1587 { } /* end */
1588};
1589
1590static struct hda_verb alc880_test_init_verbs[] = {
1591 /* Unmute inputs of 0x0c - 0x0f */
Takashi Iwai05acb862005-06-10 19:50:25 +02001592 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1593 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1594 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1595 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1596 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1597 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1598 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1599 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001600 /* Vol output for 0x0c-0x0f */
Takashi Iwai05acb862005-06-10 19:50:25 +02001601 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1602 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1603 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1604 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001605 /* Set output pins 0x14-0x17 */
Takashi Iwai05acb862005-06-10 19:50:25 +02001606 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1607 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1608 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1609 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001610 /* Unmute output pins 0x14-0x17 */
Takashi Iwai05acb862005-06-10 19:50:25 +02001611 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1612 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1613 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1614 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001615 /* Set input pins 0x18-0x1c */
Takashi Iwai16ded522005-06-10 19:58:24 +02001616 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1617 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai05acb862005-06-10 19:50:25 +02001618 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1619 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1620 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001621 /* Mute input pins 0x18-0x1b */
Takashi Iwai05acb862005-06-10 19:50:25 +02001622 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1623 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1624 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1625 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Takashi Iwai71fe7b82005-05-25 18:11:40 +02001626 /* ADC set up */
Takashi Iwai05acb862005-06-10 19:50:25 +02001627 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +02001628 {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwai05acb862005-06-10 19:50:25 +02001629 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +02001630 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwai05acb862005-06-10 19:50:25 +02001631 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +02001632 {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwai05acb862005-06-10 19:50:25 +02001633 /* Analog input/passthru */
1634 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
1635 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
1636 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
1637 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
1638 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001639 { }
1640};
1641#endif
1642
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643/*
1644 */
1645
1646static struct hda_board_config alc880_cfg_tbl[] = {
1647 /* Back 3 jack, front 2 jack */
1648 { .modelname = "3stack", .config = ALC880_3ST },
Takashi Iwai72915482005-05-12 16:49:45 +02001649 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe200, .config = ALC880_3ST },
1650 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe201, .config = ALC880_3ST },
1651 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe202, .config = ALC880_3ST },
1652 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe203, .config = ALC880_3ST },
1653 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe204, .config = ALC880_3ST },
1654 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe205, .config = ALC880_3ST },
1655 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe206, .config = ALC880_3ST },
1656 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe207, .config = ALC880_3ST },
1657 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe208, .config = ALC880_3ST },
1658 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe209, .config = ALC880_3ST },
1659 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20a, .config = ALC880_3ST },
1660 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20b, .config = ALC880_3ST },
1661 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20c, .config = ALC880_3ST },
1662 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20d, .config = ALC880_3ST },
1663 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20e, .config = ALC880_3ST },
1664 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20f, .config = ALC880_3ST },
1665 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe210, .config = ALC880_3ST },
1666 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe211, .config = ALC880_3ST },
1667 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe214, .config = ALC880_3ST },
1668 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe302, .config = ALC880_3ST },
1669 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe303, .config = ALC880_3ST },
1670 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe304, .config = ALC880_3ST },
1671 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe306, .config = ALC880_3ST },
1672 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe307, .config = ALC880_3ST },
1673 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe404, .config = ALC880_3ST },
1674 { .pci_subvendor = 0x8086, .pci_subdevice = 0xa101, .config = ALC880_3ST },
1675 { .pci_subvendor = 0x107b, .pci_subdevice = 0x3031, .config = ALC880_3ST },
1676 { .pci_subvendor = 0x107b, .pci_subdevice = 0x4036, .config = ALC880_3ST },
1677 { .pci_subvendor = 0x107b, .pci_subdevice = 0x4037, .config = ALC880_3ST },
1678 { .pci_subvendor = 0x107b, .pci_subdevice = 0x4038, .config = ALC880_3ST },
1679 { .pci_subvendor = 0x107b, .pci_subdevice = 0x4040, .config = ALC880_3ST },
1680 { .pci_subvendor = 0x107b, .pci_subdevice = 0x4041, .config = ALC880_3ST },
Kailang Yangdf694da2005-12-05 19:42:22 +01001681 /* TCL S700 */
1682 { .pci_subvendor = 0x19db, .pci_subdevice = 0x4188, .config = ALC880_TCL_S700 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683
1684 /* Back 3 jack, front 2 jack (Internal add Aux-In) */
Takashi Iwai72915482005-05-12 16:49:45 +02001685 { .pci_subvendor = 0x1025, .pci_subdevice = 0xe310, .config = ALC880_3ST },
Takashi Iwai16ded522005-06-10 19:58:24 +02001686 { .pci_subvendor = 0x104d, .pci_subdevice = 0x81d6, .config = ALC880_3ST },
Davide Libenzi0ca21612005-09-05 11:56:47 +02001687 { .pci_subvendor = 0x104d, .pci_subdevice = 0x81a0, .config = ALC880_3ST },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688
1689 /* Back 3 jack plus 1 SPDIF out jack, front 2 jack */
1690 { .modelname = "3stack-digout", .config = ALC880_3ST_DIG },
Takashi Iwai72915482005-05-12 16:49:45 +02001691 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe308, .config = ALC880_3ST_DIG },
Jaroslav Kysela5a47fe3c2005-08-15 20:01:40 +02001692 { .pci_subvendor = 0x1025, .pci_subdevice = 0x0070, .config = ALC880_3ST_DIG },
Kailang Yangdf694da2005-12-05 19:42:22 +01001693 /* Clevo m520G NB */
1694 { .pci_subvendor = 0x1558, .pci_subdevice = 0x0520, .config = ALC880_CLEVO },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695
1696 /* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/
Takashi Iwai72915482005-05-12 16:49:45 +02001697 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe305, .config = ALC880_3ST_DIG },
1698 { .pci_subvendor = 0x8086, .pci_subdevice = 0xd402, .config = ALC880_3ST_DIG },
1699 { .pci_subvendor = 0x1025, .pci_subdevice = 0xe309, .config = ALC880_3ST_DIG },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700
1701 /* Back 5 jack, front 2 jack */
1702 { .modelname = "5stack", .config = ALC880_5ST },
Takashi Iwai72915482005-05-12 16:49:45 +02001703 { .pci_subvendor = 0x107b, .pci_subdevice = 0x3033, .config = ALC880_5ST },
1704 { .pci_subvendor = 0x107b, .pci_subdevice = 0x4039, .config = ALC880_5ST },
1705 { .pci_subvendor = 0x107b, .pci_subdevice = 0x3032, .config = ALC880_5ST },
1706 { .pci_subvendor = 0x103c, .pci_subdevice = 0x2a09, .config = ALC880_5ST },
Takashi Iwai16ded522005-06-10 19:58:24 +02001707 { .pci_subvendor = 0x1043, .pci_subdevice = 0x814e, .config = ALC880_5ST },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708
1709 /* Back 5 jack plus 1 SPDIF out jack, front 2 jack */
1710 { .modelname = "5stack-digout", .config = ALC880_5ST_DIG },
Takashi Iwai72915482005-05-12 16:49:45 +02001711 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe224, .config = ALC880_5ST_DIG },
1712 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe400, .config = ALC880_5ST_DIG },
1713 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe401, .config = ALC880_5ST_DIG },
1714 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe402, .config = ALC880_5ST_DIG },
1715 { .pci_subvendor = 0x8086, .pci_subdevice = 0xd400, .config = ALC880_5ST_DIG },
1716 { .pci_subvendor = 0x8086, .pci_subdevice = 0xd401, .config = ALC880_5ST_DIG },
1717 { .pci_subvendor = 0x8086, .pci_subdevice = 0xa100, .config = ALC880_5ST_DIG },
1718 { .pci_subvendor = 0x1565, .pci_subdevice = 0x8202, .config = ALC880_5ST_DIG },
Takashi Iwai16ded522005-06-10 19:58:24 +02001719 { .pci_subvendor = 0x1019, .pci_subdevice = 0xa880, .config = ALC880_5ST_DIG },
Takashi Iwai7a318a72005-06-28 14:16:21 +02001720 /* { .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_5ST_DIG }, */ /* conflict with 6stack */
Takashi Iwai16ded522005-06-10 19:58:24 +02001721 { .pci_subvendor = 0x1695, .pci_subdevice = 0x400d, .config = ALC880_5ST_DIG },
Takashi Iwaib0af0de2005-06-21 14:49:19 +02001722 /* note subvendor = 0 below */
1723 /* { .pci_subvendor = 0x0000, .pci_subdevice = 0x8086, .config = ALC880_5ST_DIG }, */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724
1725 { .modelname = "w810", .config = ALC880_W810 },
Takashi Iwai72915482005-05-12 16:49:45 +02001726 { .pci_subvendor = 0x161f, .pci_subdevice = 0x203d, .config = ALC880_W810 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727
Takashi Iwaidfc0ff62005-05-12 14:31:49 +02001728 { .modelname = "z71v", .config = ALC880_Z71V },
Takashi Iwai72915482005-05-12 16:49:45 +02001729 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_Z71V },
Takashi Iwaidfc0ff62005-05-12 14:31:49 +02001730
Takashi Iwaib6482d42005-06-27 15:32:43 +02001731 { .modelname = "6stack", .config = ALC880_6ST },
Takashi Iwai7632c7b2005-12-07 18:25:47 +01001732 { .pci_subvendor = 0x1043, .pci_subdevice = 0x8196, .config = ALC880_6ST }, /* ASUS P5GD1-HVM */
Kailang Yangdf694da2005-12-05 19:42:22 +01001733 { .pci_subvendor = 0x1043, .pci_subdevice = 0x81b4, .config = ALC880_6ST },
Takashi Iwai7a318a72005-06-28 14:16:21 +02001734 { .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_6ST }, /* Acer APFV */
Arnaud Patardbae2bdb2006-01-27 12:05:02 +01001735 { .pci_subvendor = 0x1458, .pci_subdevice = 0xa102, .config = ALC880_6ST }, /* Gigabyte K8N51 */
Takashi Iwaib6482d42005-06-27 15:32:43 +02001736
1737 { .modelname = "6stack-digout", .config = ALC880_6ST_DIG },
Takashi Iwai16ded522005-06-10 19:58:24 +02001738 { .pci_subvendor = 0x2668, .pci_subdevice = 0x8086, .config = ALC880_6ST_DIG },
1739 { .pci_subvendor = 0x8086, .pci_subdevice = 0x2668, .config = ALC880_6ST_DIG },
1740 { .pci_subvendor = 0x1462, .pci_subdevice = 0x1150, .config = ALC880_6ST_DIG },
1741 { .pci_subvendor = 0xe803, .pci_subdevice = 0x1019, .config = ALC880_6ST_DIG },
Kailang Yangdf694da2005-12-05 19:42:22 +01001742 { .pci_subvendor = 0x1039, .pci_subdevice = 0x1234, .config = ALC880_6ST_DIG },
1743 { .pci_subvendor = 0x1025, .pci_subdevice = 0x0077, .config = ALC880_6ST_DIG },
1744 { .pci_subvendor = 0x1025, .pci_subdevice = 0x0078, .config = ALC880_6ST_DIG },
1745 { .pci_subvendor = 0x1025, .pci_subdevice = 0x0087, .config = ALC880_6ST_DIG },
Takashi Iwai041dec02005-12-23 12:27:52 +01001746 { .pci_subvendor = 0x1297, .pci_subdevice = 0xc790, .config = ALC880_6ST_DIG }, /* Shuttle ST20G5 */
Takashi Iwai16ded522005-06-10 19:58:24 +02001747
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001748 { .modelname = "asus", .config = ALC880_ASUS },
Takashi Iwai16ded522005-06-10 19:58:24 +02001749 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_ASUS_DIG },
1750 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1973, .config = ALC880_ASUS_DIG },
1751 { .pci_subvendor = 0x1043, .pci_subdevice = 0x19b3, .config = ALC880_ASUS_DIG },
1752 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1113, .config = ALC880_ASUS_DIG },
Takashi Iwai86488112005-09-09 13:56:32 +02001753 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1173, .config = ALC880_ASUS_DIG },
Takashi Iwai16ded522005-06-10 19:58:24 +02001754 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1993, .config = ALC880_ASUS },
1755 { .pci_subvendor = 0x1043, .pci_subdevice = 0x10c3, .config = ALC880_ASUS_DIG },
1756 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1133, .config = ALC880_ASUS },
1757 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1123, .config = ALC880_ASUS_DIG },
1758 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1143, .config = ALC880_ASUS },
1759 { .pci_subvendor = 0x1043, .pci_subdevice = 0x10b3, .config = ALC880_ASUS_W1V },
Kailang Yangdf694da2005-12-05 19:42:22 +01001760 { .pci_subvendor = 0x1558, .pci_subdevice = 0x5401, .config = ALC880_ASUS_DIG2 },
Takashi Iwai16ded522005-06-10 19:58:24 +02001761
1762 { .modelname = "uniwill", .config = ALC880_UNIWILL_DIG },
1763 { .pci_subvendor = 0x1584, .pci_subdevice = 0x9050, .config = ALC880_UNIWILL_DIG },
1764
1765 { .modelname = "F1734", .config = ALC880_F1734 },
1766 { .pci_subvendor = 0x1734, .pci_subdevice = 0x107c, .config = ALC880_F1734 },
Kailang Yangdf694da2005-12-05 19:42:22 +01001767 { .pci_subvendor = 0x1584, .pci_subdevice = 0x9054, .config = ALC880_F1734 },
Takashi Iwai16ded522005-06-10 19:58:24 +02001768
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001769#ifdef CONFIG_SND_DEBUG
1770 { .modelname = "test", .config = ALC880_TEST },
1771#endif
Kailang Yangdf694da2005-12-05 19:42:22 +01001772 { .modelname = "auto", .config = ALC880_AUTO },
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001773
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774 {}
1775};
1776
Takashi Iwai16ded522005-06-10 19:58:24 +02001777/*
Kailang Yangdf694da2005-12-05 19:42:22 +01001778 * ALC880 codec presets
Takashi Iwai16ded522005-06-10 19:58:24 +02001779 */
Takashi Iwai16ded522005-06-10 19:58:24 +02001780static struct alc_config_preset alc880_presets[] = {
1781 [ALC880_3ST] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001782 .mixers = { alc880_three_stack_mixer },
1783 .init_verbs = { alc880_volume_init_verbs, alc880_pin_3stack_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02001784 .num_dacs = ARRAY_SIZE(alc880_dac_nids),
Takashi Iwai16ded522005-06-10 19:58:24 +02001785 .dac_nids = alc880_dac_nids,
Takashi Iwai16ded522005-06-10 19:58:24 +02001786 .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
1787 .channel_mode = alc880_threestack_modes,
1788 .input_mux = &alc880_capture_source,
1789 },
1790 [ALC880_3ST_DIG] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001791 .mixers = { alc880_three_stack_mixer },
1792 .init_verbs = { alc880_volume_init_verbs, alc880_pin_3stack_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02001793 .num_dacs = ARRAY_SIZE(alc880_dac_nids),
Takashi Iwai16ded522005-06-10 19:58:24 +02001794 .dac_nids = alc880_dac_nids,
1795 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwai16ded522005-06-10 19:58:24 +02001796 .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
1797 .channel_mode = alc880_threestack_modes,
1798 .input_mux = &alc880_capture_source,
1799 },
Kailang Yangdf694da2005-12-05 19:42:22 +01001800 [ALC880_TCL_S700] = {
1801 .mixers = { alc880_tcl_s700_mixer },
1802 .init_verbs = { alc880_volume_init_verbs,
1803 alc880_pin_tcl_S700_init_verbs,
1804 alc880_gpio2_init_verbs },
1805 .num_dacs = ARRAY_SIZE(alc880_dac_nids),
1806 .dac_nids = alc880_dac_nids,
1807 .hp_nid = 0x03,
1808 .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
1809 .channel_mode = alc880_2_jack_modes,
1810 .input_mux = &alc880_capture_source,
1811 },
Takashi Iwai16ded522005-06-10 19:58:24 +02001812 [ALC880_5ST] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001813 .mixers = { alc880_three_stack_mixer, alc880_five_stack_mixer},
1814 .init_verbs = { alc880_volume_init_verbs, alc880_pin_5stack_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02001815 .num_dacs = ARRAY_SIZE(alc880_dac_nids),
1816 .dac_nids = alc880_dac_nids,
Takashi Iwai16ded522005-06-10 19:58:24 +02001817 .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes),
1818 .channel_mode = alc880_fivestack_modes,
1819 .input_mux = &alc880_capture_source,
1820 },
1821 [ALC880_5ST_DIG] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001822 .mixers = { alc880_three_stack_mixer, alc880_five_stack_mixer },
1823 .init_verbs = { alc880_volume_init_verbs, alc880_pin_5stack_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02001824 .num_dacs = ARRAY_SIZE(alc880_dac_nids),
1825 .dac_nids = alc880_dac_nids,
1826 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwai16ded522005-06-10 19:58:24 +02001827 .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes),
1828 .channel_mode = alc880_fivestack_modes,
1829 .input_mux = &alc880_capture_source,
1830 },
Takashi Iwaib6482d42005-06-27 15:32:43 +02001831 [ALC880_6ST] = {
1832 .mixers = { alc880_six_stack_mixer },
1833 .init_verbs = { alc880_volume_init_verbs, alc880_pin_6stack_init_verbs },
1834 .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids),
1835 .dac_nids = alc880_6st_dac_nids,
1836 .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes),
1837 .channel_mode = alc880_sixstack_modes,
1838 .input_mux = &alc880_6stack_capture_source,
1839 },
Takashi Iwai16ded522005-06-10 19:58:24 +02001840 [ALC880_6ST_DIG] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001841 .mixers = { alc880_six_stack_mixer },
1842 .init_verbs = { alc880_volume_init_verbs, alc880_pin_6stack_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02001843 .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids),
1844 .dac_nids = alc880_6st_dac_nids,
1845 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwai16ded522005-06-10 19:58:24 +02001846 .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes),
1847 .channel_mode = alc880_sixstack_modes,
1848 .input_mux = &alc880_6stack_capture_source,
1849 },
1850 [ALC880_W810] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001851 .mixers = { alc880_w810_base_mixer },
Takashi Iwaib0af0de2005-06-21 14:49:19 +02001852 .init_verbs = { alc880_volume_init_verbs, alc880_pin_w810_init_verbs,
1853 alc880_gpio2_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02001854 .num_dacs = ARRAY_SIZE(alc880_w810_dac_nids),
1855 .dac_nids = alc880_w810_dac_nids,
1856 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwai16ded522005-06-10 19:58:24 +02001857 .num_channel_mode = ARRAY_SIZE(alc880_w810_modes),
1858 .channel_mode = alc880_w810_modes,
1859 .input_mux = &alc880_capture_source,
1860 },
1861 [ALC880_Z71V] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001862 .mixers = { alc880_z71v_mixer },
Takashi Iwaib0af0de2005-06-21 14:49:19 +02001863 .init_verbs = { alc880_volume_init_verbs, alc880_pin_z71v_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02001864 .num_dacs = ARRAY_SIZE(alc880_z71v_dac_nids),
1865 .dac_nids = alc880_z71v_dac_nids,
1866 .dig_out_nid = ALC880_DIGOUT_NID,
1867 .hp_nid = 0x03,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001868 .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
1869 .channel_mode = alc880_2_jack_modes,
Takashi Iwai16ded522005-06-10 19:58:24 +02001870 .input_mux = &alc880_capture_source,
1871 },
1872 [ALC880_F1734] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001873 .mixers = { alc880_f1734_mixer },
1874 .init_verbs = { alc880_volume_init_verbs, alc880_pin_f1734_init_verbs },
1875 .num_dacs = ARRAY_SIZE(alc880_f1734_dac_nids),
1876 .dac_nids = alc880_f1734_dac_nids,
1877 .hp_nid = 0x02,
1878 .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
1879 .channel_mode = alc880_2_jack_modes,
Takashi Iwai16ded522005-06-10 19:58:24 +02001880 .input_mux = &alc880_capture_source,
1881 },
1882 [ALC880_ASUS] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001883 .mixers = { alc880_asus_mixer },
1884 .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
1885 alc880_gpio1_init_verbs },
1886 .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
1887 .dac_nids = alc880_asus_dac_nids,
1888 .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
1889 .channel_mode = alc880_asus_modes,
Takashi Iwai16ded522005-06-10 19:58:24 +02001890 .input_mux = &alc880_capture_source,
1891 },
1892 [ALC880_ASUS_DIG] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001893 .mixers = { alc880_asus_mixer },
1894 .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
1895 alc880_gpio1_init_verbs },
1896 .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
1897 .dac_nids = alc880_asus_dac_nids,
Takashi Iwai16ded522005-06-10 19:58:24 +02001898 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001899 .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
1900 .channel_mode = alc880_asus_modes,
Takashi Iwai16ded522005-06-10 19:58:24 +02001901 .input_mux = &alc880_capture_source,
1902 },
Kailang Yangdf694da2005-12-05 19:42:22 +01001903 [ALC880_ASUS_DIG2] = {
1904 .mixers = { alc880_asus_mixer },
1905 .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
1906 alc880_gpio2_init_verbs }, /* use GPIO2 */
1907 .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
1908 .dac_nids = alc880_asus_dac_nids,
1909 .dig_out_nid = ALC880_DIGOUT_NID,
1910 .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
1911 .channel_mode = alc880_asus_modes,
1912 .input_mux = &alc880_capture_source,
1913 },
Takashi Iwai16ded522005-06-10 19:58:24 +02001914 [ALC880_ASUS_W1V] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001915 .mixers = { alc880_asus_mixer, alc880_asus_w1v_mixer },
1916 .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
1917 alc880_gpio1_init_verbs },
1918 .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
1919 .dac_nids = alc880_asus_dac_nids,
Takashi Iwai16ded522005-06-10 19:58:24 +02001920 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001921 .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
1922 .channel_mode = alc880_asus_modes,
Takashi Iwai16ded522005-06-10 19:58:24 +02001923 .input_mux = &alc880_capture_source,
1924 },
1925 [ALC880_UNIWILL_DIG] = {
Takashi Iwai3c10a9d2005-08-23 20:02:27 +02001926 .mixers = { alc880_asus_mixer, alc880_pcbeep_mixer },
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001927 .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs },
1928 .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
1929 .dac_nids = alc880_asus_dac_nids,
Takashi Iwai16ded522005-06-10 19:58:24 +02001930 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001931 .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
1932 .channel_mode = alc880_asus_modes,
Takashi Iwai16ded522005-06-10 19:58:24 +02001933 .input_mux = &alc880_capture_source,
1934 },
Kailang Yangdf694da2005-12-05 19:42:22 +01001935 [ALC880_CLEVO] = {
1936 .mixers = { alc880_three_stack_mixer },
1937 .init_verbs = { alc880_volume_init_verbs,
1938 alc880_pin_clevo_init_verbs },
1939 .num_dacs = ARRAY_SIZE(alc880_dac_nids),
1940 .dac_nids = alc880_dac_nids,
1941 .hp_nid = 0x03,
1942 .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
1943 .channel_mode = alc880_threestack_modes,
1944 .input_mux = &alc880_capture_source,
1945 },
Takashi Iwai16ded522005-06-10 19:58:24 +02001946#ifdef CONFIG_SND_DEBUG
1947 [ALC880_TEST] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001948 .mixers = { alc880_test_mixer },
1949 .init_verbs = { alc880_test_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02001950 .num_dacs = ARRAY_SIZE(alc880_test_dac_nids),
1951 .dac_nids = alc880_test_dac_nids,
1952 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwai16ded522005-06-10 19:58:24 +02001953 .num_channel_mode = ARRAY_SIZE(alc880_test_modes),
1954 .channel_mode = alc880_test_modes,
1955 .input_mux = &alc880_test_capture_source,
1956 },
1957#endif
1958};
1959
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001960/*
1961 * Automatic parse of I/O pins from the BIOS configuration
1962 */
1963
1964#define NUM_CONTROL_ALLOC 32
1965#define NUM_VERB_ALLOC 32
1966
1967enum {
1968 ALC_CTL_WIDGET_VOL,
1969 ALC_CTL_WIDGET_MUTE,
1970 ALC_CTL_BIND_MUTE,
1971};
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001972static struct snd_kcontrol_new alc880_control_templates[] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001973 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
1974 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Takashi Iwai985be542005-11-02 18:26:49 +01001975 HDA_BIND_MUTE(NULL, 0, 0, 0),
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001976};
1977
1978/* add dynamic controls */
1979static int add_control(struct alc_spec *spec, int type, const char *name, unsigned long val)
1980{
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001981 struct snd_kcontrol_new *knew;
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001982
1983 if (spec->num_kctl_used >= spec->num_kctl_alloc) {
1984 int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
1985
1986 knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
1987 if (! knew)
1988 return -ENOMEM;
1989 if (spec->kctl_alloc) {
1990 memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
1991 kfree(spec->kctl_alloc);
1992 }
1993 spec->kctl_alloc = knew;
1994 spec->num_kctl_alloc = num;
1995 }
1996
1997 knew = &spec->kctl_alloc[spec->num_kctl_used];
1998 *knew = alc880_control_templates[type];
Paulo Marques543537b2005-06-23 00:09:02 -07001999 knew->name = kstrdup(name, GFP_KERNEL);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002000 if (! knew->name)
2001 return -ENOMEM;
2002 knew->private_value = val;
2003 spec->num_kctl_used++;
2004 return 0;
2005}
2006
2007#define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17)
2008#define alc880_fixed_pin_idx(nid) ((nid) - 0x14)
2009#define alc880_is_multi_pin(nid) ((nid) >= 0x18)
2010#define alc880_multi_pin_idx(nid) ((nid) - 0x18)
2011#define alc880_is_input_pin(nid) ((nid) >= 0x18)
2012#define alc880_input_pin_idx(nid) ((nid) - 0x18)
2013#define alc880_idx_to_dac(nid) ((nid) + 0x02)
2014#define alc880_dac_to_idx(nid) ((nid) - 0x02)
2015#define alc880_idx_to_mixer(nid) ((nid) + 0x0c)
2016#define alc880_idx_to_selector(nid) ((nid) + 0x10)
2017#define ALC880_PIN_CD_NID 0x1c
2018
2019/* fill in the dac_nids table from the parsed pin configuration */
2020static int alc880_auto_fill_dac_nids(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
2021{
2022 hda_nid_t nid;
2023 int assigned[4];
2024 int i, j;
2025
2026 memset(assigned, 0, sizeof(assigned));
Takashi Iwaib0af0de2005-06-21 14:49:19 +02002027 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002028
2029 /* check the pins hardwired to audio widget */
2030 for (i = 0; i < cfg->line_outs; i++) {
2031 nid = cfg->line_out_pins[i];
2032 if (alc880_is_fixed_pin(nid)) {
2033 int idx = alc880_fixed_pin_idx(nid);
Libin Yang5014f192005-11-23 15:48:36 +01002034 spec->multiout.dac_nids[i] = alc880_idx_to_dac(idx);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002035 assigned[idx] = 1;
2036 }
2037 }
2038 /* left pins can be connect to any audio widget */
2039 for (i = 0; i < cfg->line_outs; i++) {
2040 nid = cfg->line_out_pins[i];
2041 if (alc880_is_fixed_pin(nid))
2042 continue;
2043 /* search for an empty channel */
2044 for (j = 0; j < cfg->line_outs; j++) {
2045 if (! assigned[j]) {
2046 spec->multiout.dac_nids[i] = alc880_idx_to_dac(j);
2047 assigned[j] = 1;
2048 break;
2049 }
2050 }
2051 }
2052 spec->multiout.num_dacs = cfg->line_outs;
2053 return 0;
2054}
2055
2056/* add playback controls from the parsed DAC table */
Kailang Yangdf694da2005-12-05 19:42:22 +01002057static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
2058 const struct auto_pin_cfg *cfg)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002059{
2060 char name[32];
2061 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2062 hda_nid_t nid;
2063 int i, err;
2064
2065 for (i = 0; i < cfg->line_outs; i++) {
2066 if (! spec->multiout.dac_nids[i])
2067 continue;
2068 nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i]));
2069 if (i == 2) {
2070 /* Center/LFE */
2071 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Center Playback Volume",
2072 HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0)
2073 return err;
2074 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "LFE Playback Volume",
2075 HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
2076 return err;
2077 if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "Center Playback Switch",
2078 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT))) < 0)
2079 return err;
2080 if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "LFE Playback Switch",
2081 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT))) < 0)
2082 return err;
2083 } else {
2084 sprintf(name, "%s Playback Volume", chname[i]);
2085 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
2086 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
2087 return err;
2088 sprintf(name, "%s Playback Switch", chname[i]);
2089 if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name,
2090 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2091 return err;
2092 }
2093 }
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002094 return 0;
2095}
2096
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002097/* add playback controls for speaker and HP outputs */
2098static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
2099 const char *pfx)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002100{
2101 hda_nid_t nid;
2102 int err;
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002103 char name[32];
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002104
2105 if (! pin)
2106 return 0;
2107
2108 if (alc880_is_fixed_pin(pin)) {
2109 nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
2110 if (! spec->multiout.dac_nids[0]) {
2111 /* use this as the primary output */
2112 spec->multiout.dac_nids[0] = nid;
2113 if (! spec->multiout.num_dacs)
2114 spec->multiout.num_dacs = 1;
2115 } else
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002116 /* specify the DAC as the extra output */
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002117 spec->multiout.hp_nid = nid;
2118 /* control HP volume/switch on the output mixer amp */
2119 nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin));
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002120 sprintf(name, "%s Playback Volume", pfx);
2121 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002122 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
2123 return err;
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002124 sprintf(name, "%s Playback Switch", pfx);
2125 if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002126 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2127 return err;
2128 } else if (alc880_is_multi_pin(pin)) {
2129 /* set manual connection */
2130 if (! spec->multiout.dac_nids[0]) {
2131 /* use this as the primary output */
2132 spec->multiout.dac_nids[0] = alc880_idx_to_dac(alc880_multi_pin_idx(pin));
2133 if (! spec->multiout.num_dacs)
2134 spec->multiout.num_dacs = 1;
2135 }
2136 /* we have only a switch on HP-out PIN */
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002137 sprintf(name, "%s Playback Switch", pfx);
2138 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002139 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT))) < 0)
2140 return err;
2141 }
2142 return 0;
2143}
2144
2145/* create input playback/capture controls for the given pin */
Kailang Yangdf694da2005-12-05 19:42:22 +01002146static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, const char *ctlname,
2147 int idx, hda_nid_t mix_nid)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002148{
2149 char name[32];
Kailang Yangdf694da2005-12-05 19:42:22 +01002150 int err;
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002151
2152 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002153 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
Kailang Yangdf694da2005-12-05 19:42:22 +01002154 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT))) < 0)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002155 return err;
2156 sprintf(name, "%s Playback Switch", ctlname);
2157 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
Kailang Yangdf694da2005-12-05 19:42:22 +01002158 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT))) < 0)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002159 return err;
2160 return 0;
2161}
2162
2163/* create playback/capture controls for input pins */
Kailang Yangdf694da2005-12-05 19:42:22 +01002164static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec,
2165 const struct auto_pin_cfg *cfg)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002166{
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002167 struct hda_input_mux *imux = &spec->private_imux;
Kailang Yangdf694da2005-12-05 19:42:22 +01002168 int i, err, idx;
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002169
2170 for (i = 0; i < AUTO_PIN_LAST; i++) {
2171 if (alc880_is_input_pin(cfg->input_pins[i])) {
Kailang Yangdf694da2005-12-05 19:42:22 +01002172 idx = alc880_input_pin_idx(cfg->input_pins[i]);
Takashi Iwai4a471b72005-12-07 13:56:29 +01002173 err = new_analog_input(spec, cfg->input_pins[i],
2174 auto_pin_cfg_labels[i],
Kailang Yangdf694da2005-12-05 19:42:22 +01002175 idx, 0x0b);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002176 if (err < 0)
2177 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002178 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002179 imux->items[imux->num_items].index = alc880_input_pin_idx(cfg->input_pins[i]);
2180 imux->num_items++;
2181 }
2182 }
2183 return 0;
2184}
2185
Kailang Yangdf694da2005-12-05 19:42:22 +01002186static void alc880_auto_set_output_and_unmute(struct hda_codec *codec,
2187 hda_nid_t nid, int pin_type,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002188 int dac_idx)
2189{
2190 /* set as output */
2191 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2192 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2193 /* need the manual connection? */
2194 if (alc880_is_multi_pin(nid)) {
2195 struct alc_spec *spec = codec->spec;
2196 int idx = alc880_multi_pin_idx(nid);
2197 snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0,
2198 AC_VERB_SET_CONNECT_SEL,
2199 alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx]));
2200 }
2201}
2202
2203static void alc880_auto_init_multi_out(struct hda_codec *codec)
2204{
2205 struct alc_spec *spec = codec->spec;
2206 int i;
2207
2208 for (i = 0; i < spec->autocfg.line_outs; i++) {
2209 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2210 alc880_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2211 }
2212}
2213
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002214static void alc880_auto_init_extra_out(struct hda_codec *codec)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002215{
2216 struct alc_spec *spec = codec->spec;
2217 hda_nid_t pin;
2218
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002219 pin = spec->autocfg.speaker_pin;
2220 if (pin) /* connect to front */
2221 alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002222 pin = spec->autocfg.hp_pin;
2223 if (pin) /* connect to front */
2224 alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2225}
2226
2227static void alc880_auto_init_analog_input(struct hda_codec *codec)
2228{
2229 struct alc_spec *spec = codec->spec;
2230 int i;
2231
2232 for (i = 0; i < AUTO_PIN_LAST; i++) {
2233 hda_nid_t nid = spec->autocfg.input_pins[i];
2234 if (alc880_is_input_pin(nid)) {
2235 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2236 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2237 if (nid != ALC880_PIN_CD_NID)
2238 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
2239 AMP_OUT_MUTE);
2240 }
2241 }
2242}
2243
2244/* parse the BIOS configuration and set up the alc_spec */
2245/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
2246static int alc880_parse_auto_config(struct hda_codec *codec)
2247{
2248 struct alc_spec *spec = codec->spec;
2249 int err;
Kailang Yangdf694da2005-12-05 19:42:22 +01002250 static hda_nid_t alc880_ignore[] = { 0x1d, 0 };
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002251
Kailang Yangdf694da2005-12-05 19:42:22 +01002252 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
2253 alc880_ignore)) < 0)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002254 return err;
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002255 if (! spec->autocfg.line_outs && ! spec->autocfg.speaker_pin &&
2256 ! spec->autocfg.hp_pin)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002257 return 0; /* can't find valid BIOS pin config */
Kailang Yangdf694da2005-12-05 19:42:22 +01002258
2259 if ((err = alc880_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 ||
2260 (err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002261 (err = alc880_auto_create_extra_out(spec, spec->autocfg.speaker_pin,
2262 "Speaker")) < 0 ||
2263 (err = alc880_auto_create_extra_out(spec, spec->autocfg.speaker_pin,
2264 "Headphone")) < 0 ||
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002265 (err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
2266 return err;
2267
2268 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2269
2270 if (spec->autocfg.dig_out_pin)
2271 spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
2272 if (spec->autocfg.dig_in_pin)
2273 spec->dig_in_nid = ALC880_DIGIN_NID;
2274
2275 if (spec->kctl_alloc)
2276 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
2277
2278 spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs;
2279
2280 spec->input_mux = &spec->private_imux;
2281
2282 return 1;
2283}
2284
2285/* init callback for auto-configuration model -- overriding the default init */
2286static int alc880_auto_init(struct hda_codec *codec)
2287{
2288 alc_init(codec);
2289 alc880_auto_init_multi_out(codec);
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002290 alc880_auto_init_extra_out(codec);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002291 alc880_auto_init_analog_input(codec);
2292 return 0;
2293}
2294
2295/*
2296 * OK, here we have finally the patch for ALC880
2297 */
2298
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299static int patch_alc880(struct hda_codec *codec)
2300{
2301 struct alc_spec *spec;
2302 int board_config;
Kailang Yangdf694da2005-12-05 19:42:22 +01002303 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304
Takashi Iwaie560d8d2005-09-09 14:21:46 +02002305 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306 if (spec == NULL)
2307 return -ENOMEM;
2308
2309 codec->spec = spec;
2310
2311 board_config = snd_hda_check_board_config(codec, alc880_cfg_tbl);
Takashi Iwai16ded522005-06-10 19:58:24 +02002312 if (board_config < 0 || board_config >= ALC880_MODEL_LAST) {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002313 printk(KERN_INFO "hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...\n");
2314 board_config = ALC880_AUTO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315 }
2316
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002317 if (board_config == ALC880_AUTO) {
2318 /* automatic parse from the BIOS config */
2319 err = alc880_parse_auto_config(codec);
2320 if (err < 0) {
2321 alc_free(codec);
2322 return err;
2323 } else if (! err) {
2324 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 3-stack mode...\n");
2325 board_config = ALC880_3ST;
2326 }
2327 }
2328
Kailang Yangdf694da2005-12-05 19:42:22 +01002329 if (board_config != ALC880_AUTO)
2330 setup_preset(spec, &alc880_presets[board_config]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331
2332 spec->stream_name_analog = "ALC880 Analog";
2333 spec->stream_analog_playback = &alc880_pcm_analog_playback;
2334 spec->stream_analog_capture = &alc880_pcm_analog_capture;
2335
2336 spec->stream_name_digital = "ALC880 Digital";
2337 spec->stream_digital_playback = &alc880_pcm_digital_playback;
2338 spec->stream_digital_capture = &alc880_pcm_digital_capture;
2339
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002340 if (! spec->adc_nids && spec->input_mux) {
2341 /* check whether NID 0x07 is valid */
Takashi Iwai54d17402005-11-21 16:33:22 +01002342 unsigned int wcap = get_wcaps(codec, alc880_adc_nids[0]);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002343 wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
2344 if (wcap != AC_WID_AUD_IN) {
2345 spec->adc_nids = alc880_adc_nids_alt;
2346 spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt);
2347 spec->mixers[spec->num_mixers] = alc880_capture_alt_mixer;
2348 spec->num_mixers++;
2349 } else {
2350 spec->adc_nids = alc880_adc_nids;
2351 spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
2352 spec->mixers[spec->num_mixers] = alc880_capture_mixer;
2353 spec->num_mixers++;
2354 }
2355 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356
2357 codec->patch_ops = alc_patch_ops;
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002358 if (board_config == ALC880_AUTO)
2359 codec->patch_ops.init = alc880_auto_init;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002360
2361 return 0;
2362}
2363
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002364
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365/*
2366 * ALC260 support
2367 */
2368
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002369static hda_nid_t alc260_dac_nids[1] = {
2370 /* front */
2371 0x02,
2372};
2373
2374static hda_nid_t alc260_adc_nids[1] = {
2375 /* ADC0 */
2376 0x04,
2377};
2378
Kailang Yangdf694da2005-12-05 19:42:22 +01002379static hda_nid_t alc260_adc_nids_alt[1] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002380 /* ADC1 */
2381 0x05,
2382};
2383
Kailang Yangdf694da2005-12-05 19:42:22 +01002384static hda_nid_t alc260_hp_adc_nids[2] = {
2385 /* ADC1, 0 */
2386 0x05, 0x04
2387};
2388
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01002389static hda_nid_t alc260_fujitsu_adc_nids[2] = {
2390 /* ADC0, ADC1 */
2391 0x04, 0x05
2392};
2393
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002394#define ALC260_DIGOUT_NID 0x03
2395#define ALC260_DIGIN_NID 0x06
2396
2397static struct hda_input_mux alc260_capture_source = {
2398 .num_items = 4,
2399 .items = {
2400 { "Mic", 0x0 },
2401 { "Front Mic", 0x1 },
2402 { "Line", 0x2 },
2403 { "CD", 0x4 },
2404 },
2405};
2406
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002407/* On Fujitsu S702x laptops capture only makes sense from Mic/LineIn jack
2408 * and the internal CD lines.
2409 */
2410static struct hda_input_mux alc260_fujitsu_capture_source = {
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01002411 .num_items = 3,
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002412 .items = {
2413 { "Mic/Line", 0x0 },
2414 { "CD", 0x4 },
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01002415 { "Headphone", 0x2 },
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002416 },
2417};
2418
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419/*
2420 * This is just place-holder, so there's something for alc_build_pcms to look
2421 * at when it calculates the maximum number of channels. ALC260 has no mixer
2422 * element which allows changing the channel mode, so the verb list is
2423 * never used.
2424 */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +01002425static struct hda_channel_mode alc260_modes[1] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002426 { 2, NULL },
2427};
2428
Kailang Yangdf694da2005-12-05 19:42:22 +01002429
2430/* Mixer combinations
2431 *
2432 * basic: base_output + input + pc_beep + capture
2433 * HP: base_output + input + capture_alt
2434 * HP_3013: hp_3013 + input + capture
2435 * fujitsu: fujitsu + capture
2436 */
2437
2438static struct snd_kcontrol_new alc260_base_output_mixer[] = {
Takashi Iwai05acb862005-06-10 19:50:25 +02002439 HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01002440 HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
Kailang Yangdf694da2005-12-05 19:42:22 +01002441 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
2442 HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
2443 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2444 HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT),
2445 { } /* end */
2446};
2447
2448static struct snd_kcontrol_new alc260_input_mixer[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449 HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
2450 HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
2451 HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
2452 HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
2453 HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
2454 HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
2455 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT),
2456 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457 { } /* end */
2458};
2459
Kailang Yangdf694da2005-12-05 19:42:22 +01002460static struct snd_kcontrol_new alc260_pc_beep_mixer[] = {
2461 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x07, 0x05, HDA_INPUT),
2462 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x07, 0x05, HDA_INPUT),
2463 { } /* end */
2464};
2465
2466static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
2467 HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
2468 HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
2469 HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT),
2470 HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT),
2471 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
2472 HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
2473 HDA_CODEC_VOLUME_MONO("iSpeaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2474 HDA_CODEC_MUTE_MONO("iSpeaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +02002475 { } /* end */
2476};
2477
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01002478static struct snd_kcontrol_new alc260_fujitsu_mixer[] = {
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002479 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01002480 HDA_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT),
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01002481 ALC_PIN_MODE("Headphone Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002482 HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
2483 HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
2484 HDA_CODEC_VOLUME("Mic/Line Playback Volume", 0x07, 0x0, HDA_INPUT),
2485 HDA_CODEC_MUTE("Mic/Line Playback Switch", 0x07, 0x0, HDA_INPUT),
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01002486 ALC_PIN_MODE("Mic/Line Jack Mode", 0x12, ALC_PIN_DIR_IN),
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002487 HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT),
2488 HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT),
2489 HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01002490 HDA_BIND_MUTE("Internal Speaker Playback Switch", 0x09, 2, HDA_INPUT),
Kailang Yangdf694da2005-12-05 19:42:22 +01002491 { } /* end */
2492};
2493
2494/* capture mixer elements */
2495static struct snd_kcontrol_new alc260_capture_mixer[] = {
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002496 HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT),
2497 HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT),
Kailang Yangdf694da2005-12-05 19:42:22 +01002498 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x05, 0x0, HDA_INPUT),
2499 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x05, 0x0, HDA_INPUT),
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002500 {
2501 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Kailang Yangdf694da2005-12-05 19:42:22 +01002502 /* The multiple "Capture Source" controls confuse alsamixer
2503 * So call somewhat different..
2504 * FIXME: the controls appear in the "playback" view!
2505 */
2506 /* .name = "Capture Source", */
2507 .name = "Input Source",
2508 .count = 2,
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002509 .info = alc_mux_enum_info,
2510 .get = alc_mux_enum_get,
2511 .put = alc_mux_enum_put,
2512 },
2513 { } /* end */
2514};
2515
Kailang Yangdf694da2005-12-05 19:42:22 +01002516static struct snd_kcontrol_new alc260_capture_alt_mixer[] = {
2517 HDA_CODEC_VOLUME("Capture Volume", 0x05, 0x0, HDA_INPUT),
2518 HDA_CODEC_MUTE("Capture Switch", 0x05, 0x0, HDA_INPUT),
2519 {
2520 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2521 /* The multiple "Capture Source" controls confuse alsamixer
2522 * So call somewhat different..
2523 * FIXME: the controls appear in the "playback" view!
2524 */
2525 /* .name = "Capture Source", */
2526 .name = "Input Source",
2527 .count = 1,
2528 .info = alc_mux_enum_info,
2529 .get = alc_mux_enum_get,
2530 .put = alc_mux_enum_put,
2531 },
2532 { } /* end */
2533};
2534
2535/*
2536 * initialization verbs
2537 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538static struct hda_verb alc260_init_verbs[] = {
2539 /* Line In pin widget for input */
Takashi Iwai05acb862005-06-10 19:50:25 +02002540 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002541 /* CD pin widget for input */
Takashi Iwai05acb862005-06-10 19:50:25 +02002542 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002543 /* Mic1 (rear panel) pin widget for input and vref at 80% */
Takashi Iwai16ded522005-06-10 19:58:24 +02002544 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545 /* Mic2 (front panel) pin widget for input and vref at 80% */
Takashi Iwai16ded522005-06-10 19:58:24 +02002546 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547 /* LINE-2 is used for line-out in rear */
Takashi Iwai05acb862005-06-10 19:50:25 +02002548 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 /* select line-out */
Jonathan Woithefd56f2d2006-01-24 10:35:46 +01002550 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551 /* LINE-OUT pin */
Takashi Iwai05acb862005-06-10 19:50:25 +02002552 {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553 /* enable HP */
Takashi Iwai05acb862005-06-10 19:50:25 +02002554 {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555 /* enable Mono */
Takashi Iwai05acb862005-06-10 19:50:25 +02002556 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2557 /* mute capture amp left and right */
Takashi Iwai16ded522005-06-10 19:58:24 +02002558 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559 /* set connection select to line in (default select for this ADC) */
2560 {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
Takashi Iwai16ded522005-06-10 19:58:24 +02002561 /* mute capture amp left and right */
2562 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2563 /* set connection select to line in (default select for this ADC) */
2564 {0x05, AC_VERB_SET_CONNECT_SEL, 0x02},
Takashi Iwai05acb862005-06-10 19:50:25 +02002565 /* set vol=0 Line-Out mixer amp left and right */
2566 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2567 /* unmute pin widget amp left and right (no gain on this amp) */
2568 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2569 /* set vol=0 HP mixer amp left and right */
2570 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2571 /* unmute pin widget amp left and right (no gain on this amp) */
2572 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2573 /* set vol=0 Mono mixer amp left and right */
2574 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2575 /* unmute pin widget amp left and right (no gain on this amp) */
2576 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2577 /* unmute LINE-2 out pin */
2578 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579 /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
Takashi Iwai05acb862005-06-10 19:50:25 +02002580 /* mute CD */
Takashi Iwai16ded522005-06-10 19:58:24 +02002581 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
Takashi Iwai05acb862005-06-10 19:50:25 +02002582 /* mute Line In */
Takashi Iwai16ded522005-06-10 19:58:24 +02002583 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
Takashi Iwai05acb862005-06-10 19:50:25 +02002584 /* mute Mic */
Takashi Iwai16ded522005-06-10 19:58:24 +02002585 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002586 /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
Takashi Iwai05acb862005-06-10 19:50:25 +02002587 /* mute Front out path */
2588 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2589 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2590 /* mute Headphone out path */
2591 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2592 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2593 /* mute Mono out path */
2594 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2595 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596 { }
2597};
2598
Kailang Yangdf694da2005-12-05 19:42:22 +01002599static struct hda_verb alc260_hp_init_verbs[] = {
2600 /* Headphone and output */
2601 {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
2602 /* mono output */
2603 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2604 /* Mic1 (rear panel) pin widget for input and vref at 80% */
2605 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
2606 /* Mic2 (front panel) pin widget for input and vref at 80% */
2607 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
2608 /* Line In pin widget for input */
2609 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2610 /* Line-2 pin widget for output */
2611 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2612 /* CD pin widget for input */
2613 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2614 /* unmute amp left and right */
2615 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
2616 /* set connection select to line in (default select for this ADC) */
2617 {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
2618 /* unmute Line-Out mixer amp left and right (volume = 0) */
2619 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
2620 /* mute pin widget amp left and right (no gain on this amp) */
2621 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
2622 /* unmute HP mixer amp left and right (volume = 0) */
2623 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
2624 /* mute pin widget amp left and right (no gain on this amp) */
2625 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
2626 /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
2627 /* unmute CD */
2628 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
2629 /* unmute Line In */
2630 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
2631 /* unmute Mic */
2632 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
2633 /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
2634 /* Unmute Front out path */
2635 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
2636 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
2637 /* Unmute Headphone out path */
2638 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
2639 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
2640 /* Unmute Mono out path */
2641 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
2642 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
2643 { }
2644};
2645
2646static struct hda_verb alc260_hp_3013_init_verbs[] = {
2647 /* Line out and output */
2648 {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2649 /* mono output */
2650 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2651 /* Mic1 (rear panel) pin widget for input and vref at 80% */
2652 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
2653 /* Mic2 (front panel) pin widget for input and vref at 80% */
2654 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
2655 /* Line In pin widget for input */
2656 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2657 /* Headphone pin widget for output */
2658 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
2659 /* CD pin widget for input */
2660 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2661 /* unmute amp left and right */
2662 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
2663 /* set connection select to line in (default select for this ADC) */
2664 {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
2665 /* unmute Line-Out mixer amp left and right (volume = 0) */
2666 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
2667 /* mute pin widget amp left and right (no gain on this amp) */
2668 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
2669 /* unmute HP mixer amp left and right (volume = 0) */
2670 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
2671 /* mute pin widget amp left and right (no gain on this amp) */
2672 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
2673 /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
2674 /* unmute CD */
2675 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
2676 /* unmute Line In */
2677 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
2678 /* unmute Mic */
2679 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
2680 /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
2681 /* Unmute Front out path */
2682 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
2683 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
2684 /* Unmute Headphone out path */
2685 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
2686 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
2687 /* Unmute Mono out path */
2688 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
2689 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
2690 { }
2691};
2692
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002693/* Initialisation sequence for ALC260 as configured in Fujitsu S702x
2694 * laptops.
2695 */
2696static struct hda_verb alc260_fujitsu_init_verbs[] = {
2697 /* Disable all GPIOs */
2698 {0x01, AC_VERB_SET_GPIO_MASK, 0},
2699 /* Internal speaker is connected to headphone pin */
2700 {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2701 /* Headphone/Line-out jack connects to Line1 pin; make it an output */
2702 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2703 /* Mic/Line-in jack is connected to mic1 pin, so make it an input */
2704 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2705 /* Ensure all other unused pins are disabled and muted.
2706 * Note: trying to set widget 0x15 to anything blocks all audio
2707 * output for some reason, so just leave that at the default.
2708 */
2709 {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2710 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2711 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2712 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2713 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2714 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2715 /* Disable digital (SPDIF) pins */
2716 {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
2717 {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
2718
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01002719 /* Ensure Line1 pin widget takes its input from the OUT1 sum bus
2720 * when acting as an output.
2721 */
2722 {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
2723
Takashi Iwai8b33a5a2006-02-09 11:57:01 +01002724 /* Start with output sum widgets muted and their output gains at min */
2725 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2726 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2727 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2728 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2729 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2730 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2731 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2732 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2733 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002734
2735 /* Unmute HP pin widget amp left and right (no equiv mixer ctrl) */
2736 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2737 /* Unmute Line1 pin widget amp left and right (no equiv mixer ctrl) */
2738 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01002739 /* Unmute Line1 pin widget input for when this pin is used as input
2740 * (no equiv mixer ctrl). Having input and output unmuted doesn't
2741 * seem to cause a problem.
2742 */
2743 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002744 /* Unmute pin widget used for Line-in (no equiv mixer ctrl) */
2745 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2746
2747 /* Mute capture amp left and right */
2748 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01002749 /* Set ADC connection select to match default mixer setting - line
2750 * in (on mic1 pin)
2751 */
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002752 {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
2753
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01002754 /* Do the same for the second ADC: mute capture input amp and
2755 * set ADC connection to line in
2756 */
2757 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2758 {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
2759
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002760 /* Mute all inputs to mixer widget (even unconnected ones) */
2761 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
2762 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
2763 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
2764 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
2765 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
2766 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
2767 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
2768 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
Takashi Iwai4a471b72005-12-07 13:56:29 +01002769
2770 { }
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002771};
2772
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773static struct hda_pcm_stream alc260_pcm_analog_playback = {
2774 .substreams = 1,
2775 .channels_min = 2,
2776 .channels_max = 2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777};
2778
2779static struct hda_pcm_stream alc260_pcm_analog_capture = {
2780 .substreams = 1,
2781 .channels_min = 2,
2782 .channels_max = 2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783};
2784
Takashi Iwaia3bcba32005-12-06 19:05:29 +01002785#define alc260_pcm_digital_playback alc880_pcm_digital_playback
2786#define alc260_pcm_digital_capture alc880_pcm_digital_capture
2787
Kailang Yangdf694da2005-12-05 19:42:22 +01002788/*
2789 * for BIOS auto-configuration
2790 */
2791
2792static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
2793 const char *pfx)
2794{
2795 hda_nid_t nid_vol;
2796 unsigned long vol_val, sw_val;
2797 char name[32];
2798 int err;
2799
2800 if (nid >= 0x0f && nid < 0x11) {
2801 nid_vol = nid - 0x7;
2802 vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
2803 sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
2804 } else if (nid == 0x11) {
2805 nid_vol = nid - 0x7;
2806 vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT);
2807 sw_val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
2808 } else if (nid >= 0x12 && nid <= 0x15) {
2809 nid_vol = 0x08;
2810 vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
2811 sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
2812 } else
2813 return 0; /* N/A */
2814
2815 snprintf(name, sizeof(name), "%s Playback Volume", pfx);
2816 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val)) < 0)
2817 return err;
2818 snprintf(name, sizeof(name), "%s Playback Switch", pfx);
2819 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, sw_val)) < 0)
2820 return err;
2821 return 1;
2822}
2823
2824/* add playback controls from the parsed DAC table */
2825static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
2826 const struct auto_pin_cfg *cfg)
2827{
2828 hda_nid_t nid;
2829 int err;
2830
2831 spec->multiout.num_dacs = 1;
2832 spec->multiout.dac_nids = spec->private_dac_nids;
2833 spec->multiout.dac_nids[0] = 0x02;
2834
2835 nid = cfg->line_out_pins[0];
2836 if (nid) {
2837 err = alc260_add_playback_controls(spec, nid, "Front");
2838 if (err < 0)
2839 return err;
2840 }
2841
2842 nid = cfg->speaker_pin;
2843 if (nid) {
2844 err = alc260_add_playback_controls(spec, nid, "Speaker");
2845 if (err < 0)
2846 return err;
2847 }
2848
2849 nid = cfg->hp_pin;
2850 if (nid) {
2851 err = alc260_add_playback_controls(spec, nid, "Headphone");
2852 if (err < 0)
2853 return err;
2854 }
2855 return 0;
2856}
2857
2858/* create playback/capture controls for input pins */
2859static int alc260_auto_create_analog_input_ctls(struct alc_spec *spec,
2860 const struct auto_pin_cfg *cfg)
2861{
Kailang Yangdf694da2005-12-05 19:42:22 +01002862 struct hda_input_mux *imux = &spec->private_imux;
2863 int i, err, idx;
2864
2865 for (i = 0; i < AUTO_PIN_LAST; i++) {
2866 if (cfg->input_pins[i] >= 0x12) {
2867 idx = cfg->input_pins[i] - 0x12;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002868 err = new_analog_input(spec, cfg->input_pins[i],
2869 auto_pin_cfg_labels[i], idx, 0x07);
Kailang Yangdf694da2005-12-05 19:42:22 +01002870 if (err < 0)
2871 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002872 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Kailang Yangdf694da2005-12-05 19:42:22 +01002873 imux->items[imux->num_items].index = idx;
2874 imux->num_items++;
2875 }
2876 if ((cfg->input_pins[i] >= 0x0f) && (cfg->input_pins[i] <= 0x10)){
2877 idx = cfg->input_pins[i] - 0x09;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002878 err = new_analog_input(spec, cfg->input_pins[i],
2879 auto_pin_cfg_labels[i], idx, 0x07);
Kailang Yangdf694da2005-12-05 19:42:22 +01002880 if (err < 0)
2881 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002882 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Kailang Yangdf694da2005-12-05 19:42:22 +01002883 imux->items[imux->num_items].index = idx;
2884 imux->num_items++;
2885 }
2886 }
2887 return 0;
2888}
2889
2890static void alc260_auto_set_output_and_unmute(struct hda_codec *codec,
2891 hda_nid_t nid, int pin_type,
2892 int sel_idx)
2893{
2894 /* set as output */
2895 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2896 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2897 /* need the manual connection? */
2898 if (nid >= 0x12) {
2899 int idx = nid - 0x12;
2900 snd_hda_codec_write(codec, idx + 0x0b, 0,
2901 AC_VERB_SET_CONNECT_SEL, sel_idx);
2902
2903 }
2904}
2905
2906static void alc260_auto_init_multi_out(struct hda_codec *codec)
2907{
2908 struct alc_spec *spec = codec->spec;
2909 hda_nid_t nid;
2910
2911 nid = spec->autocfg.line_out_pins[0];
2912 if (nid)
2913 alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
2914
2915 nid = spec->autocfg.speaker_pin;
2916 if (nid)
2917 alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
2918
2919 nid = spec->autocfg.hp_pin;
2920 if (nid)
2921 alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
2922}
2923
2924#define ALC260_PIN_CD_NID 0x16
2925static void alc260_auto_init_analog_input(struct hda_codec *codec)
2926{
2927 struct alc_spec *spec = codec->spec;
2928 int i;
2929
2930 for (i = 0; i < AUTO_PIN_LAST; i++) {
2931 hda_nid_t nid = spec->autocfg.input_pins[i];
2932 if (nid >= 0x12) {
2933 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2934 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2935 if (nid != ALC260_PIN_CD_NID)
2936 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
2937 AMP_OUT_MUTE);
2938 }
2939 }
2940}
2941
2942/*
2943 * generic initialization of ADC, input mixers and output mixers
2944 */
2945static struct hda_verb alc260_volume_init_verbs[] = {
2946 /*
2947 * Unmute ADC0-1 and set the default input to mic-in
2948 */
2949 {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
2950 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2951 {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
2952 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2953
2954 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2955 * mixer widget
2956 * Note: PASD motherboards uses the Line In 2 as the input for front panel
2957 * mic (mic 2)
2958 */
2959 /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
2960 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2961 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2962 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2963 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2964 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2965
2966 /*
2967 * Set up output mixers (0x08 - 0x0a)
2968 */
2969 /* set vol=0 to output mixers */
2970 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2971 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2972 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2973 /* set up input amps for analog loopback */
2974 /* Amp Indices: DAC = 0, mixer = 1 */
2975 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2976 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2977 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2978 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2979 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2980 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2981
2982 { }
2983};
2984
2985static int alc260_parse_auto_config(struct hda_codec *codec)
2986{
2987 struct alc_spec *spec = codec->spec;
2988 unsigned int wcap;
2989 int err;
2990 static hda_nid_t alc260_ignore[] = { 0x17, 0 };
2991
2992 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
2993 alc260_ignore)) < 0)
2994 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002995 if ((err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0)
2996 return err;
2997 if (! spec->kctl_alloc)
Kailang Yangdf694da2005-12-05 19:42:22 +01002998 return 0; /* can't find valid BIOS pin config */
Takashi Iwai4a471b72005-12-07 13:56:29 +01002999 if ((err = alc260_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
Kailang Yangdf694da2005-12-05 19:42:22 +01003000 return err;
3001
3002 spec->multiout.max_channels = 2;
3003
3004 if (spec->autocfg.dig_out_pin)
3005 spec->multiout.dig_out_nid = ALC260_DIGOUT_NID;
3006 if (spec->kctl_alloc)
3007 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
3008
3009 spec->init_verbs[spec->num_init_verbs++] = alc260_volume_init_verbs;
3010
3011 spec->input_mux = &spec->private_imux;
3012
3013 /* check whether NID 0x04 is valid */
Takashi Iwai4a471b72005-12-07 13:56:29 +01003014 wcap = get_wcaps(codec, 0x04);
Kailang Yangdf694da2005-12-05 19:42:22 +01003015 wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
3016 if (wcap != AC_WID_AUD_IN) {
3017 spec->adc_nids = alc260_adc_nids_alt;
3018 spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
3019 spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer;
Kailang Yangdf694da2005-12-05 19:42:22 +01003020 } else {
3021 spec->adc_nids = alc260_adc_nids;
3022 spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
3023 spec->mixers[spec->num_mixers] = alc260_capture_mixer;
Kailang Yangdf694da2005-12-05 19:42:22 +01003024 }
Takashi Iwai4a471b72005-12-07 13:56:29 +01003025 spec->num_mixers++;
Kailang Yangdf694da2005-12-05 19:42:22 +01003026
3027 return 1;
3028}
3029
3030/* init callback for auto-configuration model -- overriding the default init */
3031static int alc260_auto_init(struct hda_codec *codec)
3032{
3033 alc_init(codec);
3034 alc260_auto_init_multi_out(codec);
3035 alc260_auto_init_analog_input(codec);
3036 return 0;
3037}
3038
3039/*
3040 * ALC260 configurations
3041 */
Takashi Iwai16ded522005-06-10 19:58:24 +02003042static struct hda_board_config alc260_cfg_tbl[] = {
Kailang Yangdf694da2005-12-05 19:42:22 +01003043 { .modelname = "basic", .config = ALC260_BASIC },
Takashi Iwaib14e77e2006-01-11 18:10:50 +01003044 { .pci_subvendor = 0x104d, .pci_subdevice = 0x81bb,
3045 .config = ALC260_BASIC }, /* Sony VAIO */
Takashi Iwai16ded522005-06-10 19:58:24 +02003046 { .modelname = "hp", .config = ALC260_HP },
Kailang Yangdf694da2005-12-05 19:42:22 +01003047 { .pci_subvendor = 0x103c, .pci_subdevice = 0x3010, .config = ALC260_HP },
3048 { .pci_subvendor = 0x103c, .pci_subdevice = 0x3011, .config = ALC260_HP },
3049 { .pci_subvendor = 0x103c, .pci_subdevice = 0x3012, .config = ALC260_HP },
3050 { .pci_subvendor = 0x103c, .pci_subdevice = 0x3013, .config = ALC260_HP_3013 },
3051 { .pci_subvendor = 0x103c, .pci_subdevice = 0x3014, .config = ALC260_HP },
3052 { .pci_subvendor = 0x103c, .pci_subdevice = 0x3015, .config = ALC260_HP },
3053 { .pci_subvendor = 0x103c, .pci_subdevice = 0x3016, .config = ALC260_HP },
3054 { .modelname = "fujitsu", .config = ALC260_FUJITSU_S702X },
3055 { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1326, .config = ALC260_FUJITSU_S702X },
3056 { .modelname = "auto", .config = ALC260_AUTO },
Takashi Iwai16ded522005-06-10 19:58:24 +02003057 {}
3058};
3059
Kailang Yangdf694da2005-12-05 19:42:22 +01003060static struct alc_config_preset alc260_presets[] = {
3061 [ALC260_BASIC] = {
3062 .mixers = { alc260_base_output_mixer,
3063 alc260_input_mixer,
3064 alc260_pc_beep_mixer,
3065 alc260_capture_mixer },
3066 .init_verbs = { alc260_init_verbs },
3067 .num_dacs = ARRAY_SIZE(alc260_dac_nids),
3068 .dac_nids = alc260_dac_nids,
3069 .num_adc_nids = ARRAY_SIZE(alc260_adc_nids),
3070 .adc_nids = alc260_adc_nids,
3071 .num_channel_mode = ARRAY_SIZE(alc260_modes),
3072 .channel_mode = alc260_modes,
3073 .input_mux = &alc260_capture_source,
3074 },
3075 [ALC260_HP] = {
3076 .mixers = { alc260_base_output_mixer,
3077 alc260_input_mixer,
3078 alc260_capture_alt_mixer },
3079 .init_verbs = { alc260_hp_init_verbs },
3080 .num_dacs = ARRAY_SIZE(alc260_dac_nids),
3081 .dac_nids = alc260_dac_nids,
3082 .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
3083 .adc_nids = alc260_hp_adc_nids,
3084 .num_channel_mode = ARRAY_SIZE(alc260_modes),
3085 .channel_mode = alc260_modes,
3086 .input_mux = &alc260_capture_source,
3087 },
3088 [ALC260_HP_3013] = {
3089 .mixers = { alc260_hp_3013_mixer,
3090 alc260_input_mixer,
3091 alc260_capture_alt_mixer },
3092 .init_verbs = { alc260_hp_3013_init_verbs },
3093 .num_dacs = ARRAY_SIZE(alc260_dac_nids),
3094 .dac_nids = alc260_dac_nids,
3095 .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
3096 .adc_nids = alc260_hp_adc_nids,
3097 .num_channel_mode = ARRAY_SIZE(alc260_modes),
3098 .channel_mode = alc260_modes,
3099 .input_mux = &alc260_capture_source,
3100 },
3101 [ALC260_FUJITSU_S702X] = {
3102 .mixers = { alc260_fujitsu_mixer,
3103 alc260_capture_mixer },
3104 .init_verbs = { alc260_fujitsu_init_verbs },
3105 .num_dacs = ARRAY_SIZE(alc260_dac_nids),
3106 .dac_nids = alc260_dac_nids,
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01003107 .num_adc_nids = ARRAY_SIZE(alc260_fujitsu_adc_nids),
3108 .adc_nids = alc260_fujitsu_adc_nids,
Kailang Yangdf694da2005-12-05 19:42:22 +01003109 .num_channel_mode = ARRAY_SIZE(alc260_modes),
3110 .channel_mode = alc260_modes,
3111 .input_mux = &alc260_fujitsu_capture_source,
3112 },
3113};
3114
Linus Torvalds1da177e2005-04-16 15:20:36 -07003115static int patch_alc260(struct hda_codec *codec)
3116{
3117 struct alc_spec *spec;
Kailang Yangdf694da2005-12-05 19:42:22 +01003118 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003119
Takashi Iwaie560d8d2005-09-09 14:21:46 +02003120 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003121 if (spec == NULL)
3122 return -ENOMEM;
3123
3124 codec->spec = spec;
3125
Takashi Iwai16ded522005-06-10 19:58:24 +02003126 board_config = snd_hda_check_board_config(codec, alc260_cfg_tbl);
3127 if (board_config < 0 || board_config >= ALC260_MODEL_LAST) {
3128 snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260\n");
Kailang Yangdf694da2005-12-05 19:42:22 +01003129 board_config = ALC260_AUTO;
Takashi Iwai16ded522005-06-10 19:58:24 +02003130 }
3131
Kailang Yangdf694da2005-12-05 19:42:22 +01003132 if (board_config == ALC260_AUTO) {
3133 /* automatic parse from the BIOS config */
3134 err = alc260_parse_auto_config(codec);
3135 if (err < 0) {
3136 alc_free(codec);
3137 return err;
3138 } else if (! err) {
3139 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
3140 board_config = ALC260_BASIC;
3141 }
Takashi Iwai16ded522005-06-10 19:58:24 +02003142 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003143
Kailang Yangdf694da2005-12-05 19:42:22 +01003144 if (board_config != ALC260_AUTO)
3145 setup_preset(spec, &alc260_presets[board_config]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003146
3147 spec->stream_name_analog = "ALC260 Analog";
3148 spec->stream_analog_playback = &alc260_pcm_analog_playback;
3149 spec->stream_analog_capture = &alc260_pcm_analog_capture;
3150
Takashi Iwaia3bcba32005-12-06 19:05:29 +01003151 spec->stream_name_digital = "ALC260 Digital";
3152 spec->stream_digital_playback = &alc260_pcm_digital_playback;
3153 spec->stream_digital_capture = &alc260_pcm_digital_capture;
3154
Linus Torvalds1da177e2005-04-16 15:20:36 -07003155 codec->patch_ops = alc_patch_ops;
Kailang Yangdf694da2005-12-05 19:42:22 +01003156 if (board_config == ALC260_AUTO)
3157 codec->patch_ops.init = alc260_auto_init;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003158
3159 return 0;
3160}
3161
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003162
Linus Torvalds1da177e2005-04-16 15:20:36 -07003163/*
3164 * ALC882 support
3165 *
3166 * ALC882 is almost identical with ALC880 but has cleaner and more flexible
3167 * configuration. Each pin widget can choose any input DACs and a mixer.
3168 * Each ADC is connected from a mixer of all inputs. This makes possible
3169 * 6-channel independent captures.
3170 *
3171 * In addition, an independent DAC for the multi-playback (not used in this
3172 * driver yet).
3173 */
Kailang Yangdf694da2005-12-05 19:42:22 +01003174#define ALC882_DIGOUT_NID 0x06
3175#define ALC882_DIGIN_NID 0x0a
Linus Torvalds1da177e2005-04-16 15:20:36 -07003176
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +01003177static struct hda_channel_mode alc882_ch_modes[1] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003178 { 8, NULL }
3179};
3180
3181static hda_nid_t alc882_dac_nids[4] = {
3182 /* front, rear, clfe, rear_surr */
3183 0x02, 0x03, 0x04, 0x05
3184};
3185
Kailang Yangdf694da2005-12-05 19:42:22 +01003186/* identical with ALC880 */
3187#define alc882_adc_nids alc880_adc_nids
3188#define alc882_adc_nids_alt alc880_adc_nids_alt
Linus Torvalds1da177e2005-04-16 15:20:36 -07003189
3190/* input MUX */
3191/* FIXME: should be a matrix-type input source selection */
3192
3193static struct hda_input_mux alc882_capture_source = {
3194 .num_items = 4,
3195 .items = {
3196 { "Mic", 0x0 },
3197 { "Front Mic", 0x1 },
3198 { "Line", 0x2 },
3199 { "CD", 0x4 },
3200 },
3201};
3202
3203#define alc882_mux_enum_info alc_mux_enum_info
3204#define alc882_mux_enum_get alc_mux_enum_get
3205
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01003206static int alc882_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003207{
3208 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3209 struct alc_spec *spec = codec->spec;
3210 const struct hda_input_mux *imux = spec->input_mux;
3211 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
3212 static hda_nid_t capture_mixers[3] = { 0x24, 0x23, 0x22 };
3213 hda_nid_t nid = capture_mixers[adc_idx];
3214 unsigned int *cur_val = &spec->cur_mux[adc_idx];
3215 unsigned int i, idx;
3216
3217 idx = ucontrol->value.enumerated.item[0];
3218 if (idx >= imux->num_items)
3219 idx = imux->num_items - 1;
3220 if (*cur_val == idx && ! codec->in_resume)
3221 return 0;
3222 for (i = 0; i < imux->num_items; i++) {
3223 unsigned int v = (i == idx) ? 0x7000 : 0x7080;
3224 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
3225 v | (imux->items[i].index << 8));
3226 }
3227 *cur_val = idx;
3228 return 1;
3229}
3230
Kailang Yangdf694da2005-12-05 19:42:22 +01003231/*
3232 * 6ch mode
3233 */
3234static struct hda_verb alc882_sixstack_ch6_init[] = {
3235 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
3236 { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
3237 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
3238 { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
3239 { } /* end */
3240};
3241
3242/*
3243 * 8ch mode
3244 */
3245static struct hda_verb alc882_sixstack_ch8_init[] = {
3246 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
3247 { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
3248 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
3249 { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
3250 { } /* end */
3251};
3252
3253static struct hda_channel_mode alc882_sixstack_modes[2] = {
3254 { 6, alc882_sixstack_ch6_init },
3255 { 8, alc882_sixstack_ch8_init },
3256};
3257
Linus Torvalds1da177e2005-04-16 15:20:36 -07003258/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
3259 * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
3260 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01003261static struct snd_kcontrol_new alc882_base_mixer[] = {
Takashi Iwai05acb862005-06-10 19:50:25 +02003262 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01003263 HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
Takashi Iwai05acb862005-06-10 19:50:25 +02003264 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01003265 HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
Takashi Iwai05acb862005-06-10 19:50:25 +02003266 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
3267 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01003268 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
3269 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
Takashi Iwai05acb862005-06-10 19:50:25 +02003270 HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01003271 HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -07003272 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
3273 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
3274 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
3275 HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
3276 HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
3277 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
3278 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
3279 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
3280 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
3281 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
3282 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
3283 HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
3284 HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
3285 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
3286 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
3287 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
3288 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
3289 {
3290 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3291 /* .name = "Capture Source", */
3292 .name = "Input Source",
3293 .count = 3,
3294 .info = alc882_mux_enum_info,
3295 .get = alc882_mux_enum_get,
3296 .put = alc882_mux_enum_put,
3297 },
3298 { } /* end */
3299};
3300
Kailang Yangdf694da2005-12-05 19:42:22 +01003301static struct snd_kcontrol_new alc882_chmode_mixer[] = {
3302 {
3303 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3304 .name = "Channel Mode",
3305 .info = alc_ch_mode_info,
3306 .get = alc_ch_mode_get,
3307 .put = alc_ch_mode_put,
3308 },
3309 { } /* end */
3310};
3311
Linus Torvalds1da177e2005-04-16 15:20:36 -07003312static struct hda_verb alc882_init_verbs[] = {
3313 /* Front mixer: unmute input/output amp left and right (volume = 0) */
Takashi Iwai05acb862005-06-10 19:50:25 +02003314 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3315 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3316 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003317 /* Rear mixer */
Takashi Iwai05acb862005-06-10 19:50:25 +02003318 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3319 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3320 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003321 /* CLFE mixer */
Takashi Iwai05acb862005-06-10 19:50:25 +02003322 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3323 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3324 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003325 /* Side mixer */
Takashi Iwai05acb862005-06-10 19:50:25 +02003326 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3327 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3328 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003329
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003330 /* Front Pin: output 0 (0x0c) */
Takashi Iwai05acb862005-06-10 19:50:25 +02003331 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai05acb862005-06-10 19:50:25 +02003332 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003333 {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003334 /* Rear Pin: output 1 (0x0d) */
Takashi Iwai05acb862005-06-10 19:50:25 +02003335 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai05acb862005-06-10 19:50:25 +02003336 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003337 {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003338 /* CLFE Pin: output 2 (0x0e) */
Takashi Iwai05acb862005-06-10 19:50:25 +02003339 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai05acb862005-06-10 19:50:25 +02003340 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003341 {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003342 /* Side Pin: output 3 (0x0f) */
Takashi Iwai05acb862005-06-10 19:50:25 +02003343 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai05acb862005-06-10 19:50:25 +02003344 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003345 {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003346 /* Mic (rear) pin: input vref at 80% */
Takashi Iwai16ded522005-06-10 19:58:24 +02003347 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003348 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3349 /* Front Mic pin: input vref at 80% */
Takashi Iwai16ded522005-06-10 19:58:24 +02003350 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003351 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3352 /* Line In pin: input */
Takashi Iwai05acb862005-06-10 19:50:25 +02003353 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003354 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3355 /* Line-2 In: Headphone output (output 0 - 0x0c) */
3356 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3357 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3358 {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003359 /* CD pin widget for input */
Takashi Iwai05acb862005-06-10 19:50:25 +02003360 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003361
3362 /* FIXME: use matrix-type input source selection */
3363 /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
3364 /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
Takashi Iwai05acb862005-06-10 19:50:25 +02003365 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3366 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3367 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3368 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003369 /* Input mixer2 */
Takashi Iwai05acb862005-06-10 19:50:25 +02003370 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3371 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3372 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3373 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003374 /* Input mixer3 */
Takashi Iwai05acb862005-06-10 19:50:25 +02003375 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3376 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3377 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3378 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3379 /* ADC1: mute amp left and right */
3380 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +02003381 {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwai05acb862005-06-10 19:50:25 +02003382 /* ADC2: mute amp left and right */
3383 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +02003384 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwai05acb862005-06-10 19:50:25 +02003385 /* ADC3: mute amp left and right */
3386 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +02003387 {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003388
3389 { }
3390};
3391
Kailang Yangdf694da2005-12-05 19:42:22 +01003392/*
3393 * generic initialization of ADC, input mixers and output mixers
3394 */
3395static struct hda_verb alc882_auto_init_verbs[] = {
3396 /*
3397 * Unmute ADC0-2 and set the default input to mic-in
3398 */
3399 {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
3400 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3401 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
3402 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3403 {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
3404 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3405
3406 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3407 * mixer widget
3408 * Note: PASD motherboards uses the Line In 2 as the input for front panel
3409 * mic (mic 2)
3410 */
3411 /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
3412 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3413 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3414 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3415 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3416 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3417
3418 /*
3419 * Set up output mixers (0x0c - 0x0f)
3420 */
3421 /* set vol=0 to output mixers */
3422 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3423 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3424 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3425 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3426 /* set up input amps for analog loopback */
3427 /* Amp Indices: DAC = 0, mixer = 1 */
3428 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3429 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3430 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3431 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3432 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3433 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3434 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3435 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3436 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3437 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3438
3439 /* FIXME: use matrix-type input source selection */
3440 /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
3441 /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
3442 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
3443 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
3444 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
3445 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
3446 /* Input mixer2 */
3447 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
3448 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
3449 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
3450 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
3451 /* Input mixer3 */
3452 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
3453 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
3454 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
3455 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
3456
3457 { }
3458};
3459
3460/* capture mixer elements */
3461static struct snd_kcontrol_new alc882_capture_alt_mixer[] = {
3462 HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
3463 HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
3464 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
3465 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
3466 {
3467 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3468 /* The multiple "Capture Source" controls confuse alsamixer
3469 * So call somewhat different..
3470 * FIXME: the controls appear in the "playback" view!
3471 */
3472 /* .name = "Capture Source", */
3473 .name = "Input Source",
3474 .count = 2,
3475 .info = alc882_mux_enum_info,
3476 .get = alc882_mux_enum_get,
3477 .put = alc882_mux_enum_put,
3478 },
3479 { } /* end */
3480};
3481
3482static struct snd_kcontrol_new alc882_capture_mixer[] = {
3483 HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
3484 HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
3485 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
3486 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
3487 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
3488 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
3489 {
3490 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3491 /* The multiple "Capture Source" controls confuse alsamixer
3492 * So call somewhat different..
3493 * FIXME: the controls appear in the "playback" view!
3494 */
3495 /* .name = "Capture Source", */
3496 .name = "Input Source",
3497 .count = 3,
3498 .info = alc882_mux_enum_info,
3499 .get = alc882_mux_enum_get,
3500 .put = alc882_mux_enum_put,
3501 },
3502 { } /* end */
3503};
3504
3505/* pcm configuration: identiacal with ALC880 */
3506#define alc882_pcm_analog_playback alc880_pcm_analog_playback
3507#define alc882_pcm_analog_capture alc880_pcm_analog_capture
3508#define alc882_pcm_digital_playback alc880_pcm_digital_playback
3509#define alc882_pcm_digital_capture alc880_pcm_digital_capture
3510
3511/*
3512 * configuration and preset
3513 */
3514static struct hda_board_config alc882_cfg_tbl[] = {
Takashi Iwai1494a922006-01-31 10:58:46 +01003515 { .modelname = "3stack-dig", .config = ALC882_3ST_DIG },
3516 { .modelname = "6stack-dig", .config = ALC882_6ST_DIG },
Kailang Yangdf694da2005-12-05 19:42:22 +01003517 { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* MSI */
3518 { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* Foxconn */
3519 { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* ECS */
Takashi Iwai1494a922006-01-31 10:58:46 +01003520 { .modelname = "auto", .config = ALC882_AUTO },
Kailang Yangdf694da2005-12-05 19:42:22 +01003521 {}
3522};
3523
3524static struct alc_config_preset alc882_presets[] = {
3525 [ALC882_3ST_DIG] = {
3526 .mixers = { alc882_base_mixer },
3527 .init_verbs = { alc882_init_verbs },
3528 .num_dacs = ARRAY_SIZE(alc882_dac_nids),
3529 .dac_nids = alc882_dac_nids,
3530 .dig_out_nid = ALC882_DIGOUT_NID,
3531 .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
3532 .adc_nids = alc882_adc_nids,
3533 .dig_in_nid = ALC882_DIGIN_NID,
3534 .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
3535 .channel_mode = alc882_ch_modes,
3536 .input_mux = &alc882_capture_source,
3537 },
3538 [ALC882_6ST_DIG] = {
3539 .mixers = { alc882_base_mixer, alc882_chmode_mixer },
3540 .init_verbs = { alc882_init_verbs },
3541 .num_dacs = ARRAY_SIZE(alc882_dac_nids),
3542 .dac_nids = alc882_dac_nids,
3543 .dig_out_nid = ALC882_DIGOUT_NID,
3544 .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
3545 .adc_nids = alc882_adc_nids,
3546 .dig_in_nid = ALC882_DIGIN_NID,
3547 .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
3548 .channel_mode = alc882_sixstack_modes,
3549 .input_mux = &alc882_capture_source,
3550 },
3551};
3552
3553
3554/*
3555 * BIOS auto configuration
3556 */
3557static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
3558 hda_nid_t nid, int pin_type,
3559 int dac_idx)
3560{
3561 /* set as output */
3562 struct alc_spec *spec = codec->spec;
3563 int idx;
3564
3565 if (spec->multiout.dac_nids[dac_idx] == 0x25)
3566 idx = 4;
3567 else
3568 idx = spec->multiout.dac_nids[dac_idx] - 2;
3569
3570 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
3571 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
3572 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
3573
3574}
3575
3576static void alc882_auto_init_multi_out(struct hda_codec *codec)
3577{
3578 struct alc_spec *spec = codec->spec;
3579 int i;
3580
3581 for (i = 0; i <= HDA_SIDE; i++) {
3582 hda_nid_t nid = spec->autocfg.line_out_pins[i];
3583 if (nid)
3584 alc882_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
3585 }
3586}
3587
3588static void alc882_auto_init_hp_out(struct hda_codec *codec)
3589{
3590 struct alc_spec *spec = codec->spec;
3591 hda_nid_t pin;
3592
3593 pin = spec->autocfg.hp_pin;
3594 if (pin) /* connect to front */
3595 alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); /* use dac 0 */
3596}
3597
3598#define alc882_is_input_pin(nid) alc880_is_input_pin(nid)
3599#define ALC882_PIN_CD_NID ALC880_PIN_CD_NID
3600
3601static void alc882_auto_init_analog_input(struct hda_codec *codec)
3602{
3603 struct alc_spec *spec = codec->spec;
3604 int i;
3605
3606 for (i = 0; i < AUTO_PIN_LAST; i++) {
3607 hda_nid_t nid = spec->autocfg.input_pins[i];
3608 if (alc882_is_input_pin(nid)) {
3609 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
3610 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
3611 if (nid != ALC882_PIN_CD_NID)
3612 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
3613 AMP_OUT_MUTE);
3614 }
3615 }
3616}
3617
3618/* almost identical with ALC880 parser... */
3619static int alc882_parse_auto_config(struct hda_codec *codec)
3620{
3621 struct alc_spec *spec = codec->spec;
3622 int err = alc880_parse_auto_config(codec);
3623
3624 if (err < 0)
3625 return err;
Takashi Iwaic5f2ea02005-12-06 18:54:31 +01003626 else if (err > 0)
3627 /* hack - override the init verbs */
3628 spec->init_verbs[0] = alc882_auto_init_verbs;
3629 return err;
Kailang Yangdf694da2005-12-05 19:42:22 +01003630}
3631
3632/* init callback for auto-configuration model -- overriding the default init */
3633static int alc882_auto_init(struct hda_codec *codec)
3634{
3635 alc_init(codec);
3636 alc882_auto_init_multi_out(codec);
3637 alc882_auto_init_hp_out(codec);
3638 alc882_auto_init_analog_input(codec);
3639 return 0;
3640}
3641
3642/*
3643 * ALC882 Headphone poll in 3.5.1a or 3.5.2
3644 */
3645
Linus Torvalds1da177e2005-04-16 15:20:36 -07003646static int patch_alc882(struct hda_codec *codec)
3647{
3648 struct alc_spec *spec;
Kailang Yangdf694da2005-12-05 19:42:22 +01003649 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003650
Takashi Iwaie560d8d2005-09-09 14:21:46 +02003651 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003652 if (spec == NULL)
3653 return -ENOMEM;
3654
Linus Torvalds1da177e2005-04-16 15:20:36 -07003655 codec->spec = spec;
3656
Kailang Yangdf694da2005-12-05 19:42:22 +01003657 board_config = snd_hda_check_board_config(codec, alc882_cfg_tbl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003658
Kailang Yangdf694da2005-12-05 19:42:22 +01003659 if (board_config < 0 || board_config >= ALC882_MODEL_LAST) {
3660 printk(KERN_INFO "hda_codec: Unknown model for ALC882, trying auto-probe from BIOS...\n");
3661 board_config = ALC882_AUTO;
3662 }
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003663
Kailang Yangdf694da2005-12-05 19:42:22 +01003664 if (board_config == ALC882_AUTO) {
3665 /* automatic parse from the BIOS config */
3666 err = alc882_parse_auto_config(codec);
3667 if (err < 0) {
3668 alc_free(codec);
3669 return err;
3670 } else if (! err) {
3671 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
3672 board_config = ALC882_3ST_DIG;
3673 }
3674 }
3675
3676 if (board_config != ALC882_AUTO)
3677 setup_preset(spec, &alc882_presets[board_config]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003678
3679 spec->stream_name_analog = "ALC882 Analog";
Kailang Yangdf694da2005-12-05 19:42:22 +01003680 spec->stream_analog_playback = &alc882_pcm_analog_playback;
3681 spec->stream_analog_capture = &alc882_pcm_analog_capture;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003682
3683 spec->stream_name_digital = "ALC882 Digital";
Kailang Yangdf694da2005-12-05 19:42:22 +01003684 spec->stream_digital_playback = &alc882_pcm_digital_playback;
3685 spec->stream_digital_capture = &alc882_pcm_digital_capture;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003686
Kailang Yangdf694da2005-12-05 19:42:22 +01003687 if (! spec->adc_nids && spec->input_mux) {
3688 /* check whether NID 0x07 is valid */
Takashi Iwai4a471b72005-12-07 13:56:29 +01003689 unsigned int wcap = get_wcaps(codec, 0x07);
Kailang Yangdf694da2005-12-05 19:42:22 +01003690 wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
3691 if (wcap != AC_WID_AUD_IN) {
3692 spec->adc_nids = alc882_adc_nids_alt;
3693 spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt);
3694 spec->mixers[spec->num_mixers] = alc882_capture_alt_mixer;
3695 spec->num_mixers++;
3696 } else {
3697 spec->adc_nids = alc882_adc_nids;
3698 spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids);
3699 spec->mixers[spec->num_mixers] = alc882_capture_mixer;
3700 spec->num_mixers++;
3701 }
3702 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003703
3704 codec->patch_ops = alc_patch_ops;
Kailang Yangdf694da2005-12-05 19:42:22 +01003705 if (board_config == ALC882_AUTO)
3706 codec->patch_ops.init = alc882_auto_init;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003707
3708 return 0;
3709}
3710
3711/*
Kailang Yangdf694da2005-12-05 19:42:22 +01003712 * ALC262 support
3713 */
3714
3715#define ALC262_DIGOUT_NID ALC880_DIGOUT_NID
3716#define ALC262_DIGIN_NID ALC880_DIGIN_NID
3717
3718#define alc262_dac_nids alc260_dac_nids
3719#define alc262_adc_nids alc882_adc_nids
3720#define alc262_adc_nids_alt alc882_adc_nids_alt
3721
3722#define alc262_modes alc260_modes
Takashi Iwaic5f2ea02005-12-06 18:54:31 +01003723#define alc262_capture_source alc882_capture_source
Kailang Yangdf694da2005-12-05 19:42:22 +01003724
3725static struct snd_kcontrol_new alc262_base_mixer[] = {
3726 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
3727 HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
3728 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
3729 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
3730 HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
3731 HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
3732 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
3733 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
3734 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
3735 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
3736 /* HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT),
3737 HDA_CODEC_MUTE("PC Beelp Playback Switch", 0x0b, 0x05, HDA_INPUT), */
3738 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),
3739 HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
3740 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
3741 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
3742 HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
3743 HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
3744 {
3745 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3746 .name = "Capture Source",
3747 .count = 1,
3748 .info = alc882_mux_enum_info,
3749 .get = alc882_mux_enum_get,
3750 .put = alc882_mux_enum_put,
3751 },
3752 { } /* end */
3753};
3754
3755#define alc262_capture_mixer alc882_capture_mixer
3756#define alc262_capture_alt_mixer alc882_capture_alt_mixer
3757
3758/*
3759 * generic initialization of ADC, input mixers and output mixers
3760 */
3761static struct hda_verb alc262_init_verbs[] = {
3762 /*
3763 * Unmute ADC0-2 and set the default input to mic-in
3764 */
3765 {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
3766 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3767 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
3768 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3769 {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
3770 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3771
3772 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3773 * mixer widget
3774 * Note: PASD motherboards uses the Line In 2 as the input for front panel
3775 * mic (mic 2)
3776 */
3777 /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
3778 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3779 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3780 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3781 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3782 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3783
3784 /*
3785 * Set up output mixers (0x0c - 0x0e)
3786 */
3787 /* set vol=0 to output mixers */
3788 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3789 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3790 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3791 /* set up input amps for analog loopback */
3792 /* Amp Indices: DAC = 0, mixer = 1 */
3793 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3794 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3795 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3796 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3797 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3798 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3799
3800 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3801 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
3802 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3803 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
3804 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
3805 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
3806
3807 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
3808 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
3809 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
3810 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
3811 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
3812
3813 {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
3814 {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
3815
3816 /* FIXME: use matrix-type input source selection */
3817 /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
3818 /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
3819 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
3820 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
3821 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
3822 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
3823 /* Input mixer2 */
3824 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
3825 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
3826 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
3827 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
3828 /* Input mixer3 */
3829 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
3830 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
3831 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
3832 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
3833
3834 { }
3835};
3836
3837/* add playback controls from the parsed DAC table */
3838static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
3839{
3840 hda_nid_t nid;
3841 int err;
3842
3843 spec->multiout.num_dacs = 1; /* only use one dac */
3844 spec->multiout.dac_nids = spec->private_dac_nids;
3845 spec->multiout.dac_nids[0] = 2;
3846
3847 nid = cfg->line_out_pins[0];
3848 if (nid) {
3849 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Front Playback Volume",
3850 HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT))) < 0)
3851 return err;
3852 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Front Playback Switch",
3853 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
3854 return err;
3855 }
3856
3857 nid = cfg->speaker_pin;
3858 if (nid) {
3859 if (nid == 0x16) {
3860 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Speaker Playback Volume",
3861 HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT))) < 0)
3862 return err;
3863 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Speaker Playback Switch",
3864 HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
3865 return err;
3866 } else {
3867 if (! cfg->line_out_pins[0])
3868 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Speaker Playback Volume",
3869 HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT))) < 0)
3870 return err;
3871 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Speaker Playback Switch",
3872 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
3873 return err;
3874 }
3875 }
3876 nid = cfg->hp_pin;
3877 if (nid) {
3878 /* spec->multiout.hp_nid = 2; */
3879 if (nid == 0x16) {
3880 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Headphone Playback Volume",
3881 HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT))) < 0)
3882 return err;
3883 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
3884 HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
3885 return err;
3886 } else {
3887 if (! cfg->line_out_pins[0])
3888 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Headphone Playback Volume",
3889 HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT))) < 0)
3890 return err;
3891 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
3892 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
3893 return err;
3894 }
3895 }
3896 return 0;
3897}
3898
3899/* identical with ALC880 */
3900#define alc262_auto_create_analog_input_ctls alc880_auto_create_analog_input_ctls
3901
3902/*
3903 * generic initialization of ADC, input mixers and output mixers
3904 */
3905static struct hda_verb alc262_volume_init_verbs[] = {
3906 /*
3907 * Unmute ADC0-2 and set the default input to mic-in
3908 */
3909 {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
3910 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3911 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
3912 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3913 {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
3914 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3915
3916 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3917 * mixer widget
3918 * Note: PASD motherboards uses the Line In 2 as the input for front panel
3919 * mic (mic 2)
3920 */
3921 /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
3922 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3923 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3924 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3925 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3926 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3927
3928 /*
3929 * Set up output mixers (0x0c - 0x0f)
3930 */
3931 /* set vol=0 to output mixers */
3932 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3933 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3934 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3935
3936 /* set up input amps for analog loopback */
3937 /* Amp Indices: DAC = 0, mixer = 1 */
3938 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3939 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3940 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3941 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3942 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3943 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3944
3945 /* FIXME: use matrix-type input source selection */
3946 /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
3947 /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
3948 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
3949 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
3950 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
3951 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
3952 /* Input mixer2 */
3953 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
3954 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
3955 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
3956 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
3957 /* Input mixer3 */
3958 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
3959 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
3960 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
3961 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
3962
3963 { }
3964};
3965
3966/* pcm configuration: identiacal with ALC880 */
3967#define alc262_pcm_analog_playback alc880_pcm_analog_playback
3968#define alc262_pcm_analog_capture alc880_pcm_analog_capture
3969#define alc262_pcm_digital_playback alc880_pcm_digital_playback
3970#define alc262_pcm_digital_capture alc880_pcm_digital_capture
3971
3972/*
3973 * BIOS auto configuration
3974 */
3975static int alc262_parse_auto_config(struct hda_codec *codec)
3976{
3977 struct alc_spec *spec = codec->spec;
3978 int err;
3979 static hda_nid_t alc262_ignore[] = { 0x1d, 0 };
3980
3981 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
3982 alc262_ignore)) < 0)
3983 return err;
3984 if (! spec->autocfg.line_outs && ! spec->autocfg.speaker_pin &&
3985 ! spec->autocfg.hp_pin)
3986 return 0; /* can't find valid BIOS pin config */
3987 if ((err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
3988 (err = alc262_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
3989 return err;
3990
3991 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3992
3993 if (spec->autocfg.dig_out_pin)
3994 spec->multiout.dig_out_nid = ALC262_DIGOUT_NID;
3995 if (spec->autocfg.dig_in_pin)
3996 spec->dig_in_nid = ALC262_DIGIN_NID;
3997
3998 if (spec->kctl_alloc)
3999 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
4000
4001 spec->init_verbs[spec->num_init_verbs++] = alc262_volume_init_verbs;
4002 spec->input_mux = &spec->private_imux;
4003
4004 return 1;
4005}
4006
4007#define alc262_auto_init_multi_out alc882_auto_init_multi_out
4008#define alc262_auto_init_hp_out alc882_auto_init_hp_out
4009#define alc262_auto_init_analog_input alc882_auto_init_analog_input
4010
4011
4012/* init callback for auto-configuration model -- overriding the default init */
4013static int alc262_auto_init(struct hda_codec *codec)
4014{
4015 alc_init(codec);
4016 alc262_auto_init_multi_out(codec);
4017 alc262_auto_init_hp_out(codec);
4018 alc262_auto_init_analog_input(codec);
4019 return 0;
4020}
4021
4022/*
4023 * configuration and preset
4024 */
4025static struct hda_board_config alc262_cfg_tbl[] = {
4026 { .modelname = "basic", .config = ALC262_BASIC },
4027 { .modelname = "auto", .config = ALC262_AUTO },
4028 {}
4029};
4030
4031static struct alc_config_preset alc262_presets[] = {
4032 [ALC262_BASIC] = {
4033 .mixers = { alc262_base_mixer },
4034 .init_verbs = { alc262_init_verbs },
4035 .num_dacs = ARRAY_SIZE(alc262_dac_nids),
4036 .dac_nids = alc262_dac_nids,
4037 .hp_nid = 0x03,
4038 .num_channel_mode = ARRAY_SIZE(alc262_modes),
4039 .channel_mode = alc262_modes,
Takashi Iwaia3bcba32005-12-06 19:05:29 +01004040 .input_mux = &alc262_capture_source,
Kailang Yangdf694da2005-12-05 19:42:22 +01004041 },
4042};
4043
4044static int patch_alc262(struct hda_codec *codec)
4045{
4046 struct alc_spec *spec;
4047 int board_config;
4048 int err;
4049
4050 spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
4051 if (spec == NULL)
4052 return -ENOMEM;
4053
4054 codec->spec = spec;
4055#if 0
4056 /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is under-run */
4057 {
4058 int tmp;
4059 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
4060 tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
4061 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
4062 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80);
4063 }
4064#endif
4065
4066 board_config = snd_hda_check_board_config(codec, alc262_cfg_tbl);
4067 if (board_config < 0 || board_config >= ALC262_MODEL_LAST) {
4068 printk(KERN_INFO "hda_codec: Unknown model for ALC262, trying auto-probe from BIOS...\n");
4069 board_config = ALC262_AUTO;
4070 }
4071
4072 if (board_config == ALC262_AUTO) {
4073 /* automatic parse from the BIOS config */
4074 err = alc262_parse_auto_config(codec);
4075 if (err < 0) {
4076 alc_free(codec);
4077 return err;
4078 } else if (! err) {
4079 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
4080 board_config = ALC262_BASIC;
4081 }
4082 }
4083
4084 if (board_config != ALC262_AUTO)
4085 setup_preset(spec, &alc262_presets[board_config]);
4086
4087 spec->stream_name_analog = "ALC262 Analog";
4088 spec->stream_analog_playback = &alc262_pcm_analog_playback;
4089 spec->stream_analog_capture = &alc262_pcm_analog_capture;
4090
4091 spec->stream_name_digital = "ALC262 Digital";
4092 spec->stream_digital_playback = &alc262_pcm_digital_playback;
4093 spec->stream_digital_capture = &alc262_pcm_digital_capture;
4094
4095 if (! spec->adc_nids && spec->input_mux) {
4096 /* check whether NID 0x07 is valid */
Takashi Iwai4a471b72005-12-07 13:56:29 +01004097 unsigned int wcap = get_wcaps(codec, 0x07);
4098
Kailang Yangdf694da2005-12-05 19:42:22 +01004099 wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
4100 if (wcap != AC_WID_AUD_IN) {
4101 spec->adc_nids = alc262_adc_nids_alt;
4102 spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt);
4103 spec->mixers[spec->num_mixers] = alc262_capture_alt_mixer;
4104 spec->num_mixers++;
4105 } else {
4106 spec->adc_nids = alc262_adc_nids;
4107 spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids);
4108 spec->mixers[spec->num_mixers] = alc262_capture_mixer;
4109 spec->num_mixers++;
4110 }
4111 }
4112
4113 codec->patch_ops = alc_patch_ops;
4114 if (board_config == ALC262_AUTO)
4115 codec->patch_ops.init = alc262_auto_init;
4116
4117 return 0;
4118}
4119
4120
4121/*
4122 * ALC861 channel source setting (2/6 channel selection for 3-stack)
4123 */
4124
4125/*
4126 * set the path ways for 2 channel output
4127 * need to set the codec line out and mic 1 pin widgets to inputs
4128 */
4129static struct hda_verb alc861_threestack_ch2_init[] = {
4130 /* set pin widget 1Ah (line in) for input */
4131 { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
4132 /* set pin widget 18h (mic1/2) for input, for mic also enable the vref */
4133 { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
4134
4135 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
4136 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, //mic
4137 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, //line in
4138 { } /* end */
4139};
4140/*
4141 * 6ch mode
4142 * need to set the codec line out and mic 1 pin widgets to outputs
4143 */
4144static struct hda_verb alc861_threestack_ch6_init[] = {
4145 /* set pin widget 1Ah (line in) for output (Back Surround)*/
4146 { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
4147 /* set pin widget 18h (mic1) for output (CLFE)*/
4148 { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
4149
4150 { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 },
4151 { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
4152
4153 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
4154 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, //mic
4155 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, //line in
4156 { } /* end */
4157};
4158
4159static struct hda_channel_mode alc861_threestack_modes[2] = {
4160 { 2, alc861_threestack_ch2_init },
4161 { 6, alc861_threestack_ch6_init },
4162};
4163
4164/* patch-ALC861 */
4165
4166static struct snd_kcontrol_new alc861_base_mixer[] = {
4167 /* output mixer control */
4168 HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
4169 HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
4170 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
4171 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
4172 HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT),
4173
4174 /*Input mixer control */
4175 /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
4176 HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
4177 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
4178 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
4179 HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
4180 HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
4181 HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
4182 HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
4183 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
4184 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
4185
4186 /* Capture mixer control */
4187 HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
4188 HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
4189 {
4190 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4191 .name = "Capture Source",
4192 .count = 1,
4193 .info = alc_mux_enum_info,
4194 .get = alc_mux_enum_get,
4195 .put = alc_mux_enum_put,
4196 },
4197 { } /* end */
4198};
4199
4200static struct snd_kcontrol_new alc861_3ST_mixer[] = {
4201 /* output mixer control */
4202 HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
4203 HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
4204 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
4205 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
4206 /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */
4207
4208 /* Input mixer control */
4209 /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
4210 HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
4211 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
4212 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
4213 HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
4214 HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
4215 HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
4216 HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
4217 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
4218 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
4219
4220 /* Capture mixer control */
4221 HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
4222 HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
4223 {
4224 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4225 .name = "Capture Source",
4226 .count = 1,
4227 .info = alc_mux_enum_info,
4228 .get = alc_mux_enum_get,
4229 .put = alc_mux_enum_put,
4230 },
4231 {
4232 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4233 .name = "Channel Mode",
4234 .info = alc_ch_mode_info,
4235 .get = alc_ch_mode_get,
4236 .put = alc_ch_mode_put,
4237 .private_value = ARRAY_SIZE(alc861_threestack_modes),
4238 },
4239 { } /* end */
4240};
4241
4242/*
4243 * generic initialization of ADC, input mixers and output mixers
4244 */
4245static struct hda_verb alc861_base_init_verbs[] = {
4246 /*
4247 * Unmute ADC0 and set the default input to mic-in
4248 */
4249 /* port-A for surround (rear panel) */
4250 { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
4251 { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x00 },
4252 /* port-B for mic-in (rear panel) with vref */
4253 { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
4254 /* port-C for line-in (rear panel) */
4255 { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
4256 /* port-D for Front */
4257 { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
4258 { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
4259 /* port-E for HP out (front panel) */
4260 { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
4261 /* route front PCM to HP */
4262 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 },
4263 /* port-F for mic-in (front panel) with vref */
4264 { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
4265 /* port-G for CLFE (rear panel) */
4266 { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
4267 { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x00 },
4268 /* port-H for side (rear panel) */
4269 { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
4270 { 0x20, AC_VERB_SET_CONNECT_SEL, 0x00 },
4271 /* CD-in */
4272 { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
4273 /* route front mic to ADC1*/
4274 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
4275 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4276
4277 /* Unmute DAC0~3 & spdif out*/
4278 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4279 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4280 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4281 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4282 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4283
4284 /* Unmute Mixer 14 (mic) 1c (Line in)*/
4285 {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4286 {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4287 {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4288 {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4289
4290 /* Unmute Stereo Mixer 15 */
4291 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4292 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4293 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
4294 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, //Output 0~12 step
4295
4296 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4297 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4298 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4299 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4300 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4301 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4302 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4303 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4304 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, // hp used DAC 3 (Front)
4305 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
4306
4307 { }
4308};
4309
4310static struct hda_verb alc861_threestack_init_verbs[] = {
4311 /*
4312 * Unmute ADC0 and set the default input to mic-in
4313 */
4314 /* port-A for surround (rear panel) */
4315 { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
4316 /* port-B for mic-in (rear panel) with vref */
4317 { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
4318 /* port-C for line-in (rear panel) */
4319 { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
4320 /* port-D for Front */
4321 { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
4322 { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
4323 /* port-E for HP out (front panel) */
4324 { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
4325 /* route front PCM to HP */
4326 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 },
4327 /* port-F for mic-in (front panel) with vref */
4328 { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
4329 /* port-G for CLFE (rear panel) */
4330 { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
4331 /* port-H for side (rear panel) */
4332 { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
4333 /* CD-in */
4334 { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
4335 /* route front mic to ADC1*/
4336 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
4337 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4338 /* Unmute DAC0~3 & spdif out*/
4339 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4340 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4341 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4342 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4343 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4344
4345 /* Unmute Mixer 14 (mic) 1c (Line in)*/
4346 {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4347 {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4348 {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4349 {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4350
4351 /* Unmute Stereo Mixer 15 */
4352 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4353 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4354 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
4355 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, //Output 0~12 step
4356
4357 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4358 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4359 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4360 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4361 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4362 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4363 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4364 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4365 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, // hp used DAC 3 (Front)
4366 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
4367 { }
4368};
4369/*
4370 * generic initialization of ADC, input mixers and output mixers
4371 */
4372static struct hda_verb alc861_auto_init_verbs[] = {
4373 /*
4374 * Unmute ADC0 and set the default input to mic-in
4375 */
4376// {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
4377 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4378
4379 /* Unmute DAC0~3 & spdif out*/
4380 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4381 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4382 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4383 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4384 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4385
4386 /* Unmute Mixer 14 (mic) 1c (Line in)*/
4387 {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4388 {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4389 {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4390 {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4391
4392 /* Unmute Stereo Mixer 15 */
4393 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4394 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4395 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
4396 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c},
4397
4398 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4399 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4400 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4401 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4402 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4403 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4404 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4405 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4406
4407 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4408 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4409 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
4410 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
4411 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4412 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4413 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
4414 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
4415
4416 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, // set Mic 1
4417
4418 { }
4419};
4420
4421/* pcm configuration: identiacal with ALC880 */
4422#define alc861_pcm_analog_playback alc880_pcm_analog_playback
4423#define alc861_pcm_analog_capture alc880_pcm_analog_capture
4424#define alc861_pcm_digital_playback alc880_pcm_digital_playback
4425#define alc861_pcm_digital_capture alc880_pcm_digital_capture
4426
4427
4428#define ALC861_DIGOUT_NID 0x07
4429
4430static struct hda_channel_mode alc861_8ch_modes[1] = {
4431 { 8, NULL }
4432};
4433
4434static hda_nid_t alc861_dac_nids[4] = {
4435 /* front, surround, clfe, side */
4436 0x03, 0x06, 0x05, 0x04
4437};
4438
4439static hda_nid_t alc861_adc_nids[1] = {
4440 /* ADC0-2 */
4441 0x08,
4442};
4443
4444static struct hda_input_mux alc861_capture_source = {
4445 .num_items = 5,
4446 .items = {
4447 { "Mic", 0x0 },
4448 { "Front Mic", 0x3 },
4449 { "Line", 0x1 },
4450 { "CD", 0x4 },
4451 { "Mixer", 0x5 },
4452 },
4453};
4454
4455/* fill in the dac_nids table from the parsed pin configuration */
4456static int alc861_auto_fill_dac_nids(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
4457{
4458 int i;
4459 hda_nid_t nid;
4460
4461 spec->multiout.dac_nids = spec->private_dac_nids;
4462 for (i = 0; i < cfg->line_outs; i++) {
4463 nid = cfg->line_out_pins[i];
4464 if (nid) {
4465 if (i >= ARRAY_SIZE(alc861_dac_nids))
4466 continue;
4467 spec->multiout.dac_nids[i] = alc861_dac_nids[i];
4468 }
4469 }
4470 spec->multiout.num_dacs = cfg->line_outs;
4471 return 0;
4472}
4473
4474/* add playback controls from the parsed DAC table */
4475static int alc861_auto_create_multi_out_ctls(struct alc_spec *spec,
4476 const struct auto_pin_cfg *cfg)
4477{
4478 char name[32];
4479 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
4480 hda_nid_t nid;
4481 int i, idx, err;
4482
4483 for (i = 0; i < cfg->line_outs; i++) {
4484 nid = spec->multiout.dac_nids[i];
4485 if (! nid)
4486 continue;
4487 if (nid == 0x05) {
4488 /* Center/LFE */
4489 if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "Center Playback Switch",
4490 HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0)
4491 return err;
4492 if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "LFE Playback Switch",
4493 HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
4494 return err;
4495 } else {
4496 for (idx = 0; idx < ARRAY_SIZE(alc861_dac_nids) - 1; idx++)
4497 if (nid == alc861_dac_nids[idx])
4498 break;
4499 sprintf(name, "%s Playback Switch", chname[idx]);
4500 if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name,
4501 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
4502 return err;
4503 }
4504 }
4505 return 0;
4506}
4507
4508static int alc861_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin)
4509{
4510 int err;
4511 hda_nid_t nid;
4512
4513 if (! pin)
4514 return 0;
4515
4516 if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) {
4517 nid = 0x03;
4518 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
4519 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
4520 return err;
4521 spec->multiout.hp_nid = nid;
4522 }
4523 return 0;
4524}
4525
4526/* create playback/capture controls for input pins */
4527static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
4528{
Kailang Yangdf694da2005-12-05 19:42:22 +01004529 struct hda_input_mux *imux = &spec->private_imux;
4530 int i, err, idx, idx1;
4531
4532 for (i = 0; i < AUTO_PIN_LAST; i++) {
4533 switch(cfg->input_pins[i]) {
4534 case 0x0c:
4535 idx1 = 1;
4536 idx = 2; // Line In
4537 break;
4538 case 0x0f:
4539 idx1 = 2;
4540 idx = 2; // Line In
4541 break;
4542 case 0x0d:
4543 idx1 = 0;
4544 idx = 1; // Mic In
4545 break;
4546 case 0x10:
4547 idx1 = 3;
4548 idx = 1; // Mic In
4549 break;
4550 case 0x11:
4551 idx1 = 4;
4552 idx = 0; // CD
4553 break;
4554 default:
4555 continue;
4556 }
4557
Takashi Iwai4a471b72005-12-07 13:56:29 +01004558 err = new_analog_input(spec, cfg->input_pins[i],
4559 auto_pin_cfg_labels[i], idx, 0x15);
Kailang Yangdf694da2005-12-05 19:42:22 +01004560 if (err < 0)
4561 return err;
4562
Takashi Iwai4a471b72005-12-07 13:56:29 +01004563 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Kailang Yangdf694da2005-12-05 19:42:22 +01004564 imux->items[imux->num_items].index = idx1;
4565 imux->num_items++;
4566 }
4567 return 0;
4568}
4569
4570static struct snd_kcontrol_new alc861_capture_mixer[] = {
4571 HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
4572 HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
4573
4574 {
4575 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4576 /* The multiple "Capture Source" controls confuse alsamixer
4577 * So call somewhat different..
4578 *FIXME: the controls appear in the "playback" view!
4579 */
4580 /* .name = "Capture Source", */
4581 .name = "Input Source",
4582 .count = 1,
4583 .info = alc_mux_enum_info,
4584 .get = alc_mux_enum_get,
4585 .put = alc_mux_enum_put,
4586 },
4587 { } /* end */
4588};
4589
4590static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid,
4591 int pin_type, int dac_idx)
4592{
4593 /* set as output */
4594
4595 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
4596 snd_hda_codec_write(codec, dac_idx, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
4597
4598}
4599
4600static void alc861_auto_init_multi_out(struct hda_codec *codec)
4601{
4602 struct alc_spec *spec = codec->spec;
4603 int i;
4604
4605 for (i = 0; i < spec->autocfg.line_outs; i++) {
4606 hda_nid_t nid = spec->autocfg.line_out_pins[i];
4607 if (nid)
4608 alc861_auto_set_output_and_unmute(codec, nid, PIN_OUT, spec->multiout.dac_nids[i]);
4609 }
4610}
4611
4612static void alc861_auto_init_hp_out(struct hda_codec *codec)
4613{
4614 struct alc_spec *spec = codec->spec;
4615 hda_nid_t pin;
4616
4617 pin = spec->autocfg.hp_pin;
4618 if (pin) /* connect to front */
4619 alc861_auto_set_output_and_unmute(codec, pin, PIN_HP, spec->multiout.dac_nids[0]);
4620}
4621
4622static void alc861_auto_init_analog_input(struct hda_codec *codec)
4623{
4624 struct alc_spec *spec = codec->spec;
4625 int i;
4626
4627 for (i = 0; i < AUTO_PIN_LAST; i++) {
4628 hda_nid_t nid = spec->autocfg.input_pins[i];
4629 if ((nid>=0x0c) && (nid <=0x11)) {
4630 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
4631 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
4632 }
4633 }
4634}
4635
4636/* parse the BIOS configuration and set up the alc_spec */
4637/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
4638static int alc861_parse_auto_config(struct hda_codec *codec)
4639{
4640 struct alc_spec *spec = codec->spec;
4641 int err;
4642 static hda_nid_t alc861_ignore[] = { 0x1d, 0 };
4643
4644 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
4645 alc861_ignore)) < 0)
4646 return err;
4647 if (! spec->autocfg.line_outs && ! spec->autocfg.speaker_pin &&
4648 ! spec->autocfg.hp_pin)
4649 return 0; /* can't find valid BIOS pin config */
4650
4651 if ((err = alc861_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 ||
4652 (err = alc861_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
4653 (err = alc861_auto_create_hp_ctls(spec, spec->autocfg.hp_pin)) < 0 ||
4654 (err = alc861_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
4655 return err;
4656
4657 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4658
4659 if (spec->autocfg.dig_out_pin)
4660 spec->multiout.dig_out_nid = ALC861_DIGOUT_NID;
4661
4662 if (spec->kctl_alloc)
4663 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
4664
4665 spec->init_verbs[spec->num_init_verbs++] = alc861_auto_init_verbs;
4666
4667 spec->input_mux = &spec->private_imux;
4668
4669 spec->adc_nids = alc861_adc_nids;
4670 spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
4671 spec->mixers[spec->num_mixers] = alc861_capture_mixer;
4672 spec->num_mixers++;
4673
4674 return 1;
4675}
4676
4677/* init callback for auto-configuration model -- overriding the default init */
4678static int alc861_auto_init(struct hda_codec *codec)
4679{
4680 alc_init(codec);
4681 alc861_auto_init_multi_out(codec);
4682 alc861_auto_init_hp_out(codec);
4683 alc861_auto_init_analog_input(codec);
4684
4685 return 0;
4686}
4687
4688
4689/*
4690 * configuration and preset
4691 */
4692static struct hda_board_config alc861_cfg_tbl[] = {
4693 { .modelname = "3stack", .config = ALC861_3ST },
4694 { .pci_subvendor = 0x8086, .pci_subdevice = 0xd600, .config = ALC861_3ST },
4695 { .modelname = "3stack-dig", .config = ALC861_3ST_DIG },
4696 { .modelname = "6stack-dig", .config = ALC861_6ST_DIG },
4697 { .modelname = "auto", .config = ALC861_AUTO },
4698 {}
4699};
4700
4701static struct alc_config_preset alc861_presets[] = {
4702 [ALC861_3ST] = {
4703 .mixers = { alc861_3ST_mixer },
4704 .init_verbs = { alc861_threestack_init_verbs },
4705 .num_dacs = ARRAY_SIZE(alc861_dac_nids),
4706 .dac_nids = alc861_dac_nids,
4707 .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
4708 .channel_mode = alc861_threestack_modes,
4709 .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
4710 .adc_nids = alc861_adc_nids,
4711 .input_mux = &alc861_capture_source,
4712 },
4713 [ALC861_3ST_DIG] = {
4714 .mixers = { alc861_base_mixer },
4715 .init_verbs = { alc861_threestack_init_verbs },
4716 .num_dacs = ARRAY_SIZE(alc861_dac_nids),
4717 .dac_nids = alc861_dac_nids,
4718 .dig_out_nid = ALC861_DIGOUT_NID,
4719 .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
4720 .channel_mode = alc861_threestack_modes,
4721 .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
4722 .adc_nids = alc861_adc_nids,
4723 .input_mux = &alc861_capture_source,
4724 },
4725 [ALC861_6ST_DIG] = {
4726 .mixers = { alc861_base_mixer },
4727 .init_verbs = { alc861_base_init_verbs },
4728 .num_dacs = ARRAY_SIZE(alc861_dac_nids),
4729 .dac_nids = alc861_dac_nids,
4730 .dig_out_nid = ALC861_DIGOUT_NID,
4731 .num_channel_mode = ARRAY_SIZE(alc861_8ch_modes),
4732 .channel_mode = alc861_8ch_modes,
4733 .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
4734 .adc_nids = alc861_adc_nids,
4735 .input_mux = &alc861_capture_source,
4736 },
4737};
4738
4739
4740static int patch_alc861(struct hda_codec *codec)
4741{
4742 struct alc_spec *spec;
4743 int board_config;
4744 int err;
4745
4746 spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
4747 if (spec == NULL)
4748 return -ENOMEM;
4749
4750 codec->spec = spec;
4751
4752 board_config = snd_hda_check_board_config(codec, alc861_cfg_tbl);
4753 if (board_config < 0 || board_config >= ALC861_MODEL_LAST) {
4754 printk(KERN_INFO "hda_codec: Unknown model for ALC861, trying auto-probe from BIOS...\n");
4755 board_config = ALC861_AUTO;
4756 }
4757
4758 if (board_config == ALC861_AUTO) {
4759 /* automatic parse from the BIOS config */
4760 err = alc861_parse_auto_config(codec);
4761 if (err < 0) {
4762 alc_free(codec);
4763 return err;
4764 } else if (! err) {
4765 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
4766 board_config = ALC861_3ST_DIG;
4767 }
4768 }
4769
4770 if (board_config != ALC861_AUTO)
4771 setup_preset(spec, &alc861_presets[board_config]);
4772
4773 spec->stream_name_analog = "ALC861 Analog";
4774 spec->stream_analog_playback = &alc861_pcm_analog_playback;
4775 spec->stream_analog_capture = &alc861_pcm_analog_capture;
4776
4777 spec->stream_name_digital = "ALC861 Digital";
4778 spec->stream_digital_playback = &alc861_pcm_digital_playback;
4779 spec->stream_digital_capture = &alc861_pcm_digital_capture;
4780
4781 codec->patch_ops = alc_patch_ops;
4782 if (board_config == ALC861_AUTO)
4783 codec->patch_ops.init = alc861_auto_init;
4784
4785 return 0;
4786}
4787
4788/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004789 * patch entries
4790 */
4791struct hda_codec_preset snd_hda_preset_realtek[] = {
4792 { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
Kailang Yangdf694da2005-12-05 19:42:22 +01004793 { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004794 { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
4795 { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
Kailang Yangdf694da2005-12-05 19:42:22 +01004796 { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
4797 { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
4798 { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004799 {} /* terminator */
4800};