blob: 4c6c9ec8ea5bb63b654310742b7a1aa69b4ae40d [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>
Jonathan Woithe7cf51e42006-02-09 12:01:26 +01009 * Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
Linus Torvalds1da177e2005-04-16 15:20:36 -070010 *
11 * This driver is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This driver is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26#include <sound/driver.h>
27#include <linux/init.h>
28#include <linux/delay.h>
29#include <linux/slab.h>
30#include <linux/pci.h>
31#include <sound/core.h>
32#include "hda_codec.h"
33#include "hda_local.h"
34
35
36/* ALC880 board config type */
37enum {
Linus Torvalds1da177e2005-04-16 15:20:36 -070038 ALC880_3ST,
39 ALC880_3ST_DIG,
40 ALC880_5ST,
41 ALC880_5ST_DIG,
42 ALC880_W810,
Takashi Iwaidfc0ff62005-05-12 14:31:49 +020043 ALC880_Z71V,
Takashi Iwaib6482d42005-06-27 15:32:43 +020044 ALC880_6ST,
Takashi Iwai16ded522005-06-10 19:58:24 +020045 ALC880_6ST_DIG,
46 ALC880_F1734,
47 ALC880_ASUS,
48 ALC880_ASUS_DIG,
49 ALC880_ASUS_W1V,
Kailang Yangdf694da2005-12-05 19:42:22 +010050 ALC880_ASUS_DIG2,
Takashi Iwai16ded522005-06-10 19:58:24 +020051 ALC880_UNIWILL_DIG,
Kailang Yangdf694da2005-12-05 19:42:22 +010052 ALC880_CLEVO,
53 ALC880_TCL_S700,
Takashi Iwaiae6b8132006-03-03 16:47:17 +010054 ALC880_LG,
Takashi Iwaie9edcee2005-06-13 14:16:38 +020055#ifdef CONFIG_SND_DEBUG
56 ALC880_TEST,
57#endif
Kailang Yangdf694da2005-12-05 19:42:22 +010058 ALC880_AUTO,
Takashi Iwai16ded522005-06-10 19:58:24 +020059 ALC880_MODEL_LAST /* last tag */
60};
61
62/* ALC260 models */
63enum {
64 ALC260_BASIC,
65 ALC260_HP,
Kailang Yangdf694da2005-12-05 19:42:22 +010066 ALC260_HP_3013,
67 ALC260_FUJITSU_S702X,
Jonathan Woithe0bfc90e2006-02-28 11:45:11 +010068 ALC260_ACER,
Jonathan Woithe7cf51e42006-02-09 12:01:26 +010069#ifdef CONFIG_SND_DEBUG
70 ALC260_TEST,
71#endif
Kailang Yangdf694da2005-12-05 19:42:22 +010072 ALC260_AUTO,
Takashi Iwai16ded522005-06-10 19:58:24 +020073 ALC260_MODEL_LAST /* last tag */
Linus Torvalds1da177e2005-04-16 15:20:36 -070074};
75
Kailang Yangdf694da2005-12-05 19:42:22 +010076/* ALC262 models */
77enum {
78 ALC262_BASIC,
Takashi Iwai834be882006-03-01 14:16:17 +010079 ALC262_FUJITSU,
Kailang Yangdf694da2005-12-05 19:42:22 +010080 ALC262_AUTO,
81 ALC262_MODEL_LAST /* last tag */
82};
83
84/* ALC861 models */
85enum {
86 ALC861_3ST,
87 ALC861_3ST_DIG,
88 ALC861_6ST_DIG,
89 ALC861_AUTO,
90 ALC861_MODEL_LAST,
91};
92
93/* ALC882 models */
94enum {
95 ALC882_3ST_DIG,
96 ALC882_6ST_DIG,
97 ALC882_AUTO,
98 ALC882_MODEL_LAST,
99};
100
101/* for GPIO Poll */
102#define GPIO_MASK 0x03
103
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104struct alc_spec {
105 /* codec parameterization */
Kailang Yangdf694da2005-12-05 19:42:22 +0100106 struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 unsigned int num_mixers;
108
Kailang Yangdf694da2005-12-05 19:42:22 +0100109 const struct hda_verb *init_verbs[5]; /* initialization verbs
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200110 * don't forget NULL termination!
111 */
112 unsigned int num_init_verbs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113
Takashi Iwai16ded522005-06-10 19:58:24 +0200114 char *stream_name_analog; /* analog PCM stream */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 struct hda_pcm_stream *stream_analog_playback;
116 struct hda_pcm_stream *stream_analog_capture;
117
Takashi Iwai16ded522005-06-10 19:58:24 +0200118 char *stream_name_digital; /* digital PCM stream */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 struct hda_pcm_stream *stream_digital_playback;
120 struct hda_pcm_stream *stream_digital_capture;
121
122 /* playback */
Takashi Iwai16ded522005-06-10 19:58:24 +0200123 struct hda_multi_out multiout; /* playback set-up
124 * max_channels, dacs must be set
125 * dig_out_nid and hp_nid are optional
126 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127
128 /* capture */
129 unsigned int num_adc_nids;
130 hda_nid_t *adc_nids;
Takashi Iwai16ded522005-06-10 19:58:24 +0200131 hda_nid_t dig_in_nid; /* digital-in NID; optional */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132
133 /* capture source */
134 const struct hda_input_mux *input_mux;
135 unsigned int cur_mux[3];
136
137 /* channel model */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100138 const struct hda_channel_mode *channel_mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 int num_channel_mode;
140
141 /* PCM information */
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100142 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
Takashi Iwai41e41f12005-06-08 14:48:49 +0200143
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200144 /* dynamic controls, init_verbs and input_mux */
145 struct auto_pin_cfg autocfg;
146 unsigned int num_kctl_alloc, num_kctl_used;
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100147 struct snd_kcontrol_new *kctl_alloc;
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200148 struct hda_input_mux private_imux;
Kailang Yangdf694da2005-12-05 19:42:22 +0100149 hda_nid_t private_dac_nids[5];
Takashi Iwai834be882006-03-01 14:16:17 +0100150
Takashi Iwaiae6b8132006-03-03 16:47:17 +0100151 /* hooks */
152 void (*init_hook)(struct hda_codec *codec);
153 void (*unsol_event)(struct hda_codec *codec, unsigned int res);
154
Takashi Iwai834be882006-03-01 14:16:17 +0100155 /* for pin sensing */
156 unsigned int sense_updated: 1;
157 unsigned int jack_present: 1;
Kailang Yangdf694da2005-12-05 19:42:22 +0100158};
159
160/*
161 * configuration template - to be copied to the spec instance
162 */
163struct alc_config_preset {
164 struct snd_kcontrol_new *mixers[5]; /* should be identical size with spec */
165 const struct hda_verb *init_verbs[5];
166 unsigned int num_dacs;
167 hda_nid_t *dac_nids;
168 hda_nid_t dig_out_nid; /* optional */
169 hda_nid_t hp_nid; /* optional */
170 unsigned int num_adc_nids;
171 hda_nid_t *adc_nids;
172 hda_nid_t dig_in_nid;
173 unsigned int num_channel_mode;
174 const struct hda_channel_mode *channel_mode;
175 const struct hda_input_mux *input_mux;
Takashi Iwaiae6b8132006-03-03 16:47:17 +0100176 void (*unsol_event)(struct hda_codec *, unsigned int);
177 void (*init_hook)(struct hda_codec *);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178};
179
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180
181/*
182 * input MUX handling
183 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100184static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
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 return snd_hda_input_mux_info(spec->input_mux, uinfo);
189}
190
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100191static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192{
193 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
194 struct alc_spec *spec = codec->spec;
195 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
196
197 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
198 return 0;
199}
200
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100201static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202{
203 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
204 struct alc_spec *spec = codec->spec;
205 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
206 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
207 spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);
208}
209
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200210
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211/*
212 * channel mode setting
213 */
Kailang Yangdf694da2005-12-05 19:42:22 +0100214static int alc_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215{
216 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
217 struct alc_spec *spec = codec->spec;
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100218 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
219 spec->num_channel_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220}
221
Kailang Yangdf694da2005-12-05 19:42:22 +0100222static int alc_ch_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223{
224 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
225 struct alc_spec *spec = codec->spec;
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100226 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
227 spec->num_channel_mode, spec->multiout.max_channels);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228}
229
Kailang Yangdf694da2005-12-05 19:42:22 +0100230static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231{
232 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
233 struct alc_spec *spec = codec->spec;
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100234 return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
235 spec->num_channel_mode, &spec->multiout.max_channels);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236}
237
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238/*
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100239 * Control the mode of pin widget settings via the mixer. "pc" is used
240 * instead of "%" to avoid consequences of accidently treating the % as
241 * being part of a format specifier. Maximum allowed length of a value is
242 * 63 characters plus NULL terminator.
Jonathan Woithe7cf51e42006-02-09 12:01:26 +0100243 *
244 * Note: some retasking pin complexes seem to ignore requests for input
245 * states other than HiZ (eg: PIN_VREFxx) and revert to HiZ if any of these
246 * are requested. Therefore order this list so that this behaviour will not
247 * cause problems when mixer clients move through the enum sequentially.
248 * NIDs 0x0f and 0x10 have been observed to have this behaviour.
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200249 */
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100250static char *alc_pin_mode_names[] = {
Jonathan Woithe7cf51e42006-02-09 12:01:26 +0100251 "Mic 50pc bias", "Mic 80pc bias",
252 "Line in", "Line out", "Headphone out",
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100253};
254static unsigned char alc_pin_mode_values[] = {
Jonathan Woithe7cf51e42006-02-09 12:01:26 +0100255 PIN_VREF50, PIN_VREF80, PIN_IN, PIN_OUT, PIN_HP,
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100256};
257/* The control can present all 5 options, or it can limit the options based
258 * in the pin being assumed to be exclusively an input or an output pin.
259 */
260#define ALC_PIN_DIR_IN 0x00
261#define ALC_PIN_DIR_OUT 0x01
262#define ALC_PIN_DIR_INOUT 0x02
263
264/* Info about the pin modes supported by the three different pin directions.
265 * For each direction the minimum and maximum values are given.
266 */
267static signed char alc_pin_mode_dir_info[3][2] = {
268 { 0, 2 }, /* ALC_PIN_DIR_IN */
269 { 3, 4 }, /* ALC_PIN_DIR_OUT */
270 { 0, 4 }, /* ALC_PIN_DIR_INOUT */
271};
272#define alc_pin_mode_min(_dir) (alc_pin_mode_dir_info[_dir][0])
273#define alc_pin_mode_max(_dir) (alc_pin_mode_dir_info[_dir][1])
274#define alc_pin_mode_n_items(_dir) \
275 (alc_pin_mode_max(_dir)-alc_pin_mode_min(_dir)+1)
276
277static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200278{
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100279 unsigned int item_num = uinfo->value.enumerated.item;
280 unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
281
282 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200283 uinfo->count = 1;
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100284 uinfo->value.enumerated.items = alc_pin_mode_n_items(dir);
285
286 if (item_num<alc_pin_mode_min(dir) || item_num>alc_pin_mode_max(dir))
287 item_num = alc_pin_mode_min(dir);
288 strcpy(uinfo->value.enumerated.name, alc_pin_mode_names[item_num]);
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200289 return 0;
290}
291
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100292static int alc_pin_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200293{
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100294 unsigned int i;
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200295 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
296 hda_nid_t nid = kcontrol->private_value & 0xffff;
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100297 unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200298 long *valp = ucontrol->value.integer.value;
299 unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00);
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200300
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100301 /* Find enumerated value for current pinctl setting */
302 i = alc_pin_mode_min(dir);
303 while (alc_pin_mode_values[i]!=pinctl && i<=alc_pin_mode_max(dir))
304 i++;
305 *valp = i<=alc_pin_mode_max(dir)?i:alc_pin_mode_min(dir);
306 return 0;
307}
308
309static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
310{
311 signed int change;
312 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
313 hda_nid_t nid = kcontrol->private_value & 0xffff;
314 unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
315 long val = *ucontrol->value.integer.value;
316 unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00);
317
318 if (val<alc_pin_mode_min(dir) || val>alc_pin_mode_max(dir))
319 val = alc_pin_mode_min(dir);
320
321 change = pinctl != alc_pin_mode_values[val];
Jonathan Woithecdcd9262006-02-28 11:36:42 +0100322 if (change) {
323 /* Set pin mode to that requested */
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200324 snd_hda_codec_write(codec,nid,0,AC_VERB_SET_PIN_WIDGET_CONTROL,
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100325 alc_pin_mode_values[val]);
Jonathan Woithecdcd9262006-02-28 11:36:42 +0100326
327 /* Also enable the retasking pin's input/output as required
328 * for the requested pin mode. Enum values of 2 or less are
329 * input modes.
330 *
331 * Dynamically switching the input/output buffers probably
332 * reduces noise slightly, particularly on input. However,
333 * havingboth input and output buffers enabled
334 * simultaneously doesn't seem to be problematic.
335 */
336 if (val <= 2) {
337 snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE,
338 AMP_OUT_MUTE);
339 snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE,
340 AMP_IN_UNMUTE(0));
341 } else {
342 snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE,
343 AMP_IN_MUTE(0));
344 snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE,
345 AMP_OUT_UNMUTE);
346 }
347 }
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200348 return change;
349}
350
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100351#define ALC_PIN_MODE(xname, nid, dir) \
Jonathan Woithea9430dd2005-09-16 19:12:48 +0200352 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
Jonathan Woithe4c5186e2006-02-09 11:53:48 +0100353 .info = alc_pin_mode_info, \
354 .get = alc_pin_mode_get, \
355 .put = alc_pin_mode_put, \
356 .private_value = nid | (dir<<16) }
Kailang Yangdf694da2005-12-05 19:42:22 +0100357
Jonathan Woithe5c8f8582006-02-28 11:43:27 +0100358/* A switch control for ALC260 GPIO pins. Multiple GPIOs can be ganged
359 * together using a mask with more than one bit set. This control is
360 * currently used only by the ALC260 test model. At this stage they are not
361 * needed for any "production" models.
362 */
363#ifdef CONFIG_SND_DEBUG
364static int alc_gpio_data_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
365{
366 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
367 uinfo->count = 1;
368 uinfo->value.integer.min = 0;
369 uinfo->value.integer.max = 1;
370 return 0;
371}
372static int alc_gpio_data_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
373{
374 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
375 hda_nid_t nid = kcontrol->private_value & 0xffff;
376 unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
377 long *valp = ucontrol->value.integer.value;
378 unsigned int val = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_GPIO_DATA,0x00);
379
380 *valp = (val & mask) != 0;
381 return 0;
382}
383static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
384{
385 signed int change;
386 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
387 hda_nid_t nid = kcontrol->private_value & 0xffff;
388 unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
389 long val = *ucontrol->value.integer.value;
390 unsigned int gpio_data = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_GPIO_DATA,0x00);
391
392 /* Set/unset the masked GPIO bit(s) as needed */
393 change = (val==0?0:mask) != (gpio_data & mask);
394 if (val==0)
395 gpio_data &= ~mask;
396 else
397 gpio_data |= mask;
398 snd_hda_codec_write(codec,nid,0,AC_VERB_SET_GPIO_DATA,gpio_data);
399
400 return change;
401}
402#define ALC_GPIO_DATA_SWITCH(xname, nid, mask) \
403 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
404 .info = alc_gpio_data_info, \
405 .get = alc_gpio_data_get, \
406 .put = alc_gpio_data_put, \
407 .private_value = nid | (mask<<16) }
408#endif /* CONFIG_SND_DEBUG */
409
Jonathan Woithe92621f12006-02-28 11:47:47 +0100410/* A switch control to allow the enabling of the digital IO pins on the
411 * ALC260. This is incredibly simplistic; the intention of this control is
412 * to provide something in the test model allowing digital outputs to be
413 * identified if present. If models are found which can utilise these
414 * outputs a more complete mixer control can be devised for those models if
415 * necessary.
416 */
417#ifdef CONFIG_SND_DEBUG
418static int alc_spdif_ctrl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
419{
420 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
421 uinfo->count = 1;
422 uinfo->value.integer.min = 0;
423 uinfo->value.integer.max = 1;
424 return 0;
425}
426static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
427{
428 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
429 hda_nid_t nid = kcontrol->private_value & 0xffff;
430 unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
431 long *valp = ucontrol->value.integer.value;
432 unsigned int val = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_DIGI_CONVERT,0x00);
433
434 *valp = (val & mask) != 0;
435 return 0;
436}
437static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
438{
439 signed int change;
440 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
441 hda_nid_t nid = kcontrol->private_value & 0xffff;
442 unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
443 long val = *ucontrol->value.integer.value;
444 unsigned int ctrl_data = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_DIGI_CONVERT,0x00);
445
446 /* Set/unset the masked control bit(s) as needed */
447 change = (val==0?0:mask) != (ctrl_data & mask);
448 if (val==0)
449 ctrl_data &= ~mask;
450 else
451 ctrl_data |= mask;
452 snd_hda_codec_write(codec,nid,0,AC_VERB_SET_DIGI_CONVERT_1,ctrl_data);
453
454 return change;
455}
456#define ALC_SPDIF_CTRL_SWITCH(xname, nid, mask) \
457 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
458 .info = alc_spdif_ctrl_info, \
459 .get = alc_spdif_ctrl_get, \
460 .put = alc_spdif_ctrl_put, \
461 .private_value = nid | (mask<<16) }
462#endif /* CONFIG_SND_DEBUG */
463
Kailang Yangdf694da2005-12-05 19:42:22 +0100464/*
465 * set up from the preset table
466 */
467static void setup_preset(struct alc_spec *spec, const struct alc_config_preset *preset)
468{
469 int i;
470
471 for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
472 spec->mixers[spec->num_mixers++] = preset->mixers[i];
473 for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; i++)
474 spec->init_verbs[spec->num_init_verbs++] = preset->init_verbs[i];
475
476 spec->channel_mode = preset->channel_mode;
477 spec->num_channel_mode = preset->num_channel_mode;
478
479 spec->multiout.max_channels = spec->channel_mode[0].channels;
480
481 spec->multiout.num_dacs = preset->num_dacs;
482 spec->multiout.dac_nids = preset->dac_nids;
483 spec->multiout.dig_out_nid = preset->dig_out_nid;
484 spec->multiout.hp_nid = preset->hp_nid;
485
486 spec->input_mux = preset->input_mux;
487
488 spec->num_adc_nids = preset->num_adc_nids;
489 spec->adc_nids = preset->adc_nids;
490 spec->dig_in_nid = preset->dig_in_nid;
Takashi Iwaiae6b8132006-03-03 16:47:17 +0100491
492 spec->unsol_event = preset->unsol_event;
493 spec->init_hook = preset->init_hook;
Kailang Yangdf694da2005-12-05 19:42:22 +0100494}
495
Takashi Iwai41e41f12005-06-08 14:48:49 +0200496/*
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200497 * ALC880 3-stack model
498 *
499 * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e)
500 * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18, F-Mic = 0x1b
501 * HP = 0x19
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 */
503
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200504static hda_nid_t alc880_dac_nids[4] = {
505 /* front, rear, clfe, rear_surr */
506 0x02, 0x05, 0x04, 0x03
507};
508
509static hda_nid_t alc880_adc_nids[3] = {
510 /* ADC0-2 */
511 0x07, 0x08, 0x09,
512};
513
514/* The datasheet says the node 0x07 is connected from inputs,
515 * but it shows zero connection in the real implementation on some devices.
Kailang Yangdf694da2005-12-05 19:42:22 +0100516 * Note: this is a 915GAV bug, fixed on 915GLV
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 */
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200518static hda_nid_t alc880_adc_nids_alt[2] = {
519 /* ADC1-2 */
520 0x08, 0x09,
521};
522
523#define ALC880_DIGOUT_NID 0x06
524#define ALC880_DIGIN_NID 0x0a
525
526static struct hda_input_mux alc880_capture_source = {
527 .num_items = 4,
528 .items = {
529 { "Mic", 0x0 },
530 { "Front Mic", 0x3 },
531 { "Line", 0x2 },
532 { "CD", 0x4 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 },
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200534};
535
536/* channel source setting (2/6 channel selection for 3-stack) */
537/* 2ch mode */
538static struct hda_verb alc880_threestack_ch2_init[] = {
539 /* set line-in to input, mute it */
540 { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
541 { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
542 /* set mic-in to input vref 80%, mute it */
543 { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
544 { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 { } /* end */
546};
547
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200548/* 6ch mode */
549static struct hda_verb alc880_threestack_ch6_init[] = {
550 /* set line-in to output, unmute it */
551 { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
552 { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
553 /* set mic-in to output, unmute it */
554 { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
555 { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
556 { } /* end */
557};
558
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100559static struct hda_channel_mode alc880_threestack_modes[2] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200560 { 2, alc880_threestack_ch2_init },
561 { 6, alc880_threestack_ch6_init },
562};
563
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100564static struct snd_kcontrol_new alc880_three_stack_mixer[] = {
Takashi Iwai05acb862005-06-10 19:50:25 +0200565 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100566 HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
Takashi Iwai05acb862005-06-10 19:50:25 +0200567 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100568 HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT),
Takashi Iwai05acb862005-06-10 19:50:25 +0200569 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
570 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100571 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
572 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
574 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
575 HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
576 HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
577 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
578 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
579 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
580 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT),
581 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
582 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT),
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200584 {
585 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
586 .name = "Channel Mode",
Kailang Yangdf694da2005-12-05 19:42:22 +0100587 .info = alc_ch_mode_info,
588 .get = alc_ch_mode_get,
589 .put = alc_ch_mode_put,
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200590 },
591 { } /* end */
592};
593
594/* capture mixer elements */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100595static struct snd_kcontrol_new alc880_capture_mixer[] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200596 HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
597 HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
598 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
599 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
600 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
601 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
602 {
603 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
604 /* The multiple "Capture Source" controls confuse alsamixer
605 * So call somewhat different..
606 * FIXME: the controls appear in the "playback" view!
607 */
608 /* .name = "Capture Source", */
609 .name = "Input Source",
610 .count = 3,
611 .info = alc_mux_enum_info,
612 .get = alc_mux_enum_get,
613 .put = alc_mux_enum_put,
614 },
615 { } /* end */
616};
617
618/* capture mixer elements (in case NID 0x07 not available) */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100619static struct snd_kcontrol_new alc880_capture_alt_mixer[] = {
Takashi Iwai71fe7b82005-05-25 18:11:40 +0200620 HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
621 HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
622 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
623 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 {
625 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
626 /* The multiple "Capture Source" controls confuse alsamixer
627 * So call somewhat different..
628 * FIXME: the controls appear in the "playback" view!
629 */
630 /* .name = "Capture Source", */
631 .name = "Input Source",
632 .count = 2,
633 .info = alc_mux_enum_info,
634 .get = alc_mux_enum_get,
635 .put = alc_mux_enum_put,
636 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 { } /* end */
638};
639
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200640
641
642/*
643 * ALC880 5-stack model
644 *
645 * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d), Side = 0x02 (0xd)
646 * Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16
647 * Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19
648 */
649
650/* additional mixers to alc880_three_stack_mixer */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100651static struct snd_kcontrol_new alc880_five_stack_mixer[] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200652 HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100653 HDA_BIND_MUTE("Side Playback Switch", 0x0d, 2, HDA_INPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 { } /* end */
655};
656
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200657/* channel source setting (6/8 channel selection for 5-stack) */
658/* 6ch mode */
659static struct hda_verb alc880_fivestack_ch6_init[] = {
660 /* set line-in to input, mute it */
661 { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
662 { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
Takashi Iwaidfc0ff62005-05-12 14:31:49 +0200663 { } /* end */
664};
665
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200666/* 8ch mode */
667static struct hda_verb alc880_fivestack_ch8_init[] = {
668 /* set line-in to output, unmute it */
669 { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
670 { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
671 { } /* end */
672};
673
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100674static struct hda_channel_mode alc880_fivestack_modes[2] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200675 { 6, alc880_fivestack_ch6_init },
676 { 8, alc880_fivestack_ch8_init },
677};
678
679
680/*
681 * ALC880 6-stack model
682 *
683 * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e), Side = 0x05 (0x0f)
684 * Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17,
685 * Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b
686 */
687
688static hda_nid_t alc880_6st_dac_nids[4] = {
689 /* front, rear, clfe, rear_surr */
690 0x02, 0x03, 0x04, 0x05
691};
692
693static struct hda_input_mux alc880_6stack_capture_source = {
694 .num_items = 4,
695 .items = {
696 { "Mic", 0x0 },
697 { "Front Mic", 0x1 },
698 { "Line", 0x2 },
699 { "CD", 0x4 },
700 },
701};
702
703/* fixed 8-channels */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100704static struct hda_channel_mode alc880_sixstack_modes[1] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200705 { 8, NULL },
706};
707
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100708static struct snd_kcontrol_new alc880_six_stack_mixer[] = {
Takashi Iwai16ded522005-06-10 19:58:24 +0200709 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100710 HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200711 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100712 HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200713 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
714 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100715 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
716 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200717 HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100718 HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200719 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
720 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
721 HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
722 HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
723 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
724 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
725 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
726 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
727 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
728 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200729 {
730 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
731 .name = "Channel Mode",
Kailang Yangdf694da2005-12-05 19:42:22 +0100732 .info = alc_ch_mode_info,
733 .get = alc_ch_mode_get,
734 .put = alc_ch_mode_put,
Takashi Iwai16ded522005-06-10 19:58:24 +0200735 },
736 { } /* end */
737};
738
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200739
740/*
741 * ALC880 W810 model
742 *
743 * W810 has rear IO for:
744 * Front (DAC 02)
745 * Surround (DAC 03)
746 * Center/LFE (DAC 04)
747 * Digital out (06)
748 *
749 * The system also has a pair of internal speakers, and a headphone jack.
750 * These are both connected to Line2 on the codec, hence to DAC 02.
751 *
752 * There is a variable resistor to control the speaker or headphone
753 * volume. This is a hardware-only device without a software API.
754 *
755 * Plugging headphones in will disable the internal speakers. This is
756 * implemented in hardware, not via the driver using jack sense. In
757 * a similar fashion, plugging into the rear socket marked "front" will
758 * disable both the speakers and headphones.
759 *
760 * For input, there's a microphone jack, and an "audio in" jack.
761 * These may not do anything useful with this driver yet, because I
762 * haven't setup any initialization verbs for these yet...
763 */
764
765static hda_nid_t alc880_w810_dac_nids[3] = {
766 /* front, rear/surround, clfe */
767 0x02, 0x03, 0x04
768};
769
770/* fixed 6 channels */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100771static struct hda_channel_mode alc880_w810_modes[1] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200772 { 6, NULL }
773};
774
775/* Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, HP = 0x1b */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100776static struct snd_kcontrol_new alc880_w810_base_mixer[] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200777 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100778 HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200779 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100780 HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200781 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
782 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100783 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
784 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200785 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
786 { } /* end */
787};
788
789
790/*
791 * Z710V model
792 *
793 * DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d)
794 * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?), Line = 0x1a
795 */
796
797static hda_nid_t alc880_z71v_dac_nids[1] = {
798 0x02
799};
800#define ALC880_Z71V_HP_DAC 0x03
801
802/* fixed 2 channels */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +0100803static struct hda_channel_mode alc880_2_jack_modes[1] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200804 { 2, NULL }
805};
806
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100807static struct snd_kcontrol_new alc880_z71v_mixer[] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200808 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100809 HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200810 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100811 HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT),
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200812 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
813 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
814 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
815 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
816 { } /* end */
817};
818
819
820/* FIXME! */
821/*
822 * ALC880 F1734 model
823 *
824 * DAC: HP = 0x02 (0x0c), Front = 0x03 (0x0d)
825 * Pin assignment: HP = 0x14, Front = 0x15, Mic = 0x18
826 */
827
828static hda_nid_t alc880_f1734_dac_nids[1] = {
829 0x03
830};
831#define ALC880_F1734_HP_DAC 0x02
832
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100833static struct snd_kcontrol_new alc880_f1734_mixer[] = {
Takashi Iwai16ded522005-06-10 19:58:24 +0200834 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100835 HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200836 HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100837 HDA_BIND_MUTE("Internal Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200838 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
839 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
840 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
841 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200842 { } /* end */
843};
844
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200845
846/* FIXME! */
847/*
848 * ALC880 ASUS model
849 *
850 * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e)
851 * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16,
852 * Mic = 0x18, Line = 0x1a
853 */
854
855#define alc880_asus_dac_nids alc880_w810_dac_nids /* identical with w810 */
856#define alc880_asus_modes alc880_threestack_modes /* 2/6 channel mode */
857
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100858static struct snd_kcontrol_new alc880_asus_mixer[] = {
Takashi Iwai16ded522005-06-10 19:58:24 +0200859 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100860 HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200861 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100862 HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200863 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
864 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +0100865 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
866 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200867 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
868 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
869 HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
870 HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
871 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
872 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200873 {
874 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
875 .name = "Channel Mode",
Kailang Yangdf694da2005-12-05 19:42:22 +0100876 .info = alc_ch_mode_info,
877 .get = alc_ch_mode_get,
878 .put = alc_ch_mode_put,
Takashi Iwai16ded522005-06-10 19:58:24 +0200879 },
880 { } /* end */
881};
882
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200883/* FIXME! */
884/*
885 * ALC880 ASUS W1V model
886 *
887 * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e)
888 * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16,
889 * Mic = 0x18, Line = 0x1a, Line2 = 0x1b
890 */
891
892/* additional mixers to alc880_asus_mixer */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100893static struct snd_kcontrol_new alc880_asus_w1v_mixer[] = {
Takashi Iwai16ded522005-06-10 19:58:24 +0200894 HDA_CODEC_VOLUME("Line2 Playback Volume", 0x0b, 0x03, HDA_INPUT),
895 HDA_CODEC_MUTE("Line2 Playback Switch", 0x0b, 0x03, HDA_INPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +0200896 { } /* end */
897};
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200898
Takashi Iwai3c10a9d2005-08-23 20:02:27 +0200899/* additional mixers to alc880_asus_mixer */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100900static struct snd_kcontrol_new alc880_pcbeep_mixer[] = {
Takashi Iwai3c10a9d2005-08-23 20:02:27 +0200901 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
902 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
903 { } /* end */
904};
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200905
Kailang Yangdf694da2005-12-05 19:42:22 +0100906/* TCL S700 */
907static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
908 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
909 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
910 HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
911 HDA_CODEC_VOLUME("CD Playback Volume", 0x0B, 0x04, HDA_INPUT),
912 HDA_CODEC_MUTE("CD Playback Switch", 0x0B, 0x04, HDA_INPUT),
913 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0B, 0x0, HDA_INPUT),
914 HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT),
915 HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
916 HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
917 {
918 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
919 /* The multiple "Capture Source" controls confuse alsamixer
920 * So call somewhat different..
921 * FIXME: the controls appear in the "playback" view!
922 */
923 /* .name = "Capture Source", */
924 .name = "Input Source",
925 .count = 1,
926 .info = alc_mux_enum_info,
927 .get = alc_mux_enum_get,
928 .put = alc_mux_enum_put,
929 },
930 { } /* end */
931};
932
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933/*
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200934 * build control elements
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 */
936static int alc_build_controls(struct hda_codec *codec)
937{
938 struct alc_spec *spec = codec->spec;
939 int err;
940 int i;
941
942 for (i = 0; i < spec->num_mixers; i++) {
943 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
944 if (err < 0)
945 return err;
946 }
947
948 if (spec->multiout.dig_out_nid) {
949 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
950 if (err < 0)
951 return err;
952 }
953 if (spec->dig_in_nid) {
954 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
955 if (err < 0)
956 return err;
957 }
958 return 0;
959}
960
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200961
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962/*
963 * initialize the codec volumes, etc
964 */
965
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200966/*
967 * generic initialization of ADC, input mixers and output mixers
968 */
969static struct hda_verb alc880_volume_init_verbs[] = {
970 /*
971 * Unmute ADC0-2 and set the default input to mic-in
972 */
Takashi Iwai71fe7b82005-05-25 18:11:40 +0200973 {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200974 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +0200975 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200976 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +0200977 {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200978 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200980 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
981 * mixer widget
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 * Note: PASD motherboards uses the Line In 2 as the input for front panel
983 * mic (mic 2)
984 */
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200985 /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
Takashi Iwai16ded522005-06-10 19:58:24 +0200986 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200987 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
988 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
989 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
990 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200992 /*
993 * Set up output mixers (0x0c - 0x0f)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994 */
Takashi Iwaie9edcee2005-06-13 14:16:38 +0200995 /* set vol=0 to output mixers */
996 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
997 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
998 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
999 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1000 /* set up input amps for analog loopback */
1001 /* Amp Indices: DAC = 0, mixer = 1 */
Takashi Iwai05acb862005-06-10 19:50:25 +02001002 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
1003 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwai05acb862005-06-10 19:50:25 +02001004 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
1005 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwai05acb862005-06-10 19:50:25 +02001006 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
1007 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwai05acb862005-06-10 19:50:25 +02001008 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
1009 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010
1011 { }
1012};
1013
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001014/*
1015 * 3-stack pin configuration:
1016 * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b
1017 */
1018static struct hda_verb alc880_pin_3stack_init_verbs[] = {
1019 /*
1020 * preset connection lists of input pins
1021 * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround
1022 */
1023 {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
1024 {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
1025 {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */
1026
1027 /*
1028 * Set pin mode and muting
1029 */
1030 /* set front pin widgets 0x14 for output */
1031 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1032 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1033 /* Mic1 (rear panel) pin widget for input and vref at 80% */
1034 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1035 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1036 /* Mic2 (as headphone out) for HP output */
1037 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
1038 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1039 /* Line In pin widget for input */
1040 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1041 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1042 /* Line2 (as front mic) pin widget for input and vref at 80% */
1043 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1044 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1045 /* CD pin widget for input */
1046 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1047
1048 { }
1049};
1050
1051/*
1052 * 5-stack pin configuration:
1053 * front = 0x14, surround = 0x17, clfe = 0x16, mic = 0x18, HP = 0x19,
1054 * line-in/side = 0x1a, f-mic = 0x1b
1055 */
1056static struct hda_verb alc880_pin_5stack_init_verbs[] = {
1057 /*
1058 * preset connection lists of input pins
1059 * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround
1060 */
1061 {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
1062 {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/side */
1063
1064 /*
1065 * Set pin mode and muting
1066 */
1067 /* set pin widgets 0x14-0x17 for output */
Takashi Iwai05acb862005-06-10 19:50:25 +02001068 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1069 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1070 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1071 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001072 /* unmute pins for output (no gain on this amp) */
1073 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1074 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1075 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1076 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1077
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 /* Mic1 (rear panel) pin widget for input and vref at 80% */
Takashi Iwai16ded522005-06-10 19:58:24 +02001079 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001080 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1081 /* Mic2 (as headphone out) for HP output */
1082 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Takashi Iwai05acb862005-06-10 19:50:25 +02001083 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001084 /* Line In pin widget for input */
1085 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1086 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1087 /* Line2 (as front mic) pin widget for input and vref at 80% */
1088 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1089 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1090 /* CD pin widget for input */
1091 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092
1093 { }
1094};
1095
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001096/*
1097 * W810 pin configuration:
1098 * front = 0x14, surround = 0x15, clfe = 0x16, HP = 0x1b
1099 */
1100static struct hda_verb alc880_pin_w810_init_verbs[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 /* hphone/speaker input selector: front DAC */
1102 {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
1103
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001104 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1105 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1106 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1107 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1108 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1109 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1110
1111 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Takashi Iwai05acb862005-06-10 19:50:25 +02001112 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 { }
1115};
1116
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001117/*
1118 * Z71V pin configuration:
1119 * Speaker-out = 0x14, HP = 0x15, Mic = 0x18, Line-in = 0x1a, Mic2 = 0x1b (?)
1120 */
1121static struct hda_verb alc880_pin_z71v_init_verbs[] = {
Takashi Iwai05acb862005-06-10 19:50:25 +02001122 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001123 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwai05acb862005-06-10 19:50:25 +02001124 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001125 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaidfc0ff62005-05-12 14:31:49 +02001126
Takashi Iwai16ded522005-06-10 19:58:24 +02001127 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001128 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai16ded522005-06-10 19:58:24 +02001129 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001130 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidfc0ff62005-05-12 14:31:49 +02001131
1132 { }
1133};
1134
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001135/*
1136 * 6-stack pin configuration:
1137 * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18, f-mic = 0x19,
1138 * line = 0x1a, HP = 0x1b
1139 */
1140static struct hda_verb alc880_pin_6stack_init_verbs[] = {
1141 {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
1142
Takashi Iwai16ded522005-06-10 19:58:24 +02001143 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001144 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwai16ded522005-06-10 19:58:24 +02001145 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001146 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwai16ded522005-06-10 19:58:24 +02001147 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001148 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwai16ded522005-06-10 19:58:24 +02001149 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001150 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1151
Takashi Iwai16ded522005-06-10 19:58:24 +02001152 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001153 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Takashi Iwai16ded522005-06-10 19:58:24 +02001154 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001155 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Takashi Iwai16ded522005-06-10 19:58:24 +02001156 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001157 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Takashi Iwai16ded522005-06-10 19:58:24 +02001158 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Takashi Iwai16ded522005-06-10 19:58:24 +02001159 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwai16ded522005-06-10 19:58:24 +02001160 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1161
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001162 { }
1163};
Takashi Iwai16ded522005-06-10 19:58:24 +02001164
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001165/* FIXME! */
1166/*
1167 * F1734 pin configuration:
1168 * HP = 0x14, speaker-out = 0x15, mic = 0x18
1169 */
1170static struct hda_verb alc880_pin_f1734_init_verbs[] = {
1171 {0x10, AC_VERB_SET_CONNECT_SEL, 0x02},
1172 {0x11, AC_VERB_SET_CONNECT_SEL, 0x00},
1173 {0x12, AC_VERB_SET_CONNECT_SEL, 0x01},
1174 {0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
1175
1176 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
1177 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1178 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1179 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1180
1181 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1182 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1183 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1184 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1185 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1186 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1187 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1188 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1189 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai16ded522005-06-10 19:58:24 +02001190
1191 { }
1192};
1193
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001194/* FIXME! */
1195/*
1196 * ASUS pin configuration:
1197 * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a
1198 */
1199static struct hda_verb alc880_pin_asus_init_verbs[] = {
1200 {0x10, AC_VERB_SET_CONNECT_SEL, 0x02},
1201 {0x11, AC_VERB_SET_CONNECT_SEL, 0x00},
1202 {0x12, AC_VERB_SET_CONNECT_SEL, 0x01},
1203 {0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
1204
1205 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
1206 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1207 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1208 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1209 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1210 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1211 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1212 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1213
1214 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1215 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1216 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1217 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1218 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1219 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1220 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1221 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1222 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1223
1224 { }
1225};
1226
1227/* Enable GPIO mask and set output */
1228static struct hda_verb alc880_gpio1_init_verbs[] = {
1229 {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
1230 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
1231 {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
Kailang Yangdf694da2005-12-05 19:42:22 +01001232
1233 { }
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001234};
1235
1236/* Enable GPIO mask and set output */
1237static struct hda_verb alc880_gpio2_init_verbs[] = {
1238 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
1239 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
1240 {0x01, AC_VERB_SET_GPIO_DATA, 0x02},
Kailang Yangdf694da2005-12-05 19:42:22 +01001241
1242 { }
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001243};
1244
Kailang Yangdf694da2005-12-05 19:42:22 +01001245/* Clevo m520g init */
1246static struct hda_verb alc880_pin_clevo_init_verbs[] = {
1247 /* headphone output */
1248 {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
1249 /* line-out */
1250 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1251 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1252 /* Line-in */
1253 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1254 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1255 /* CD */
1256 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1257 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1258 /* Mic1 (rear panel) */
1259 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1260 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1261 /* Mic2 (front panel) */
1262 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1263 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1264 /* headphone */
1265 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
1266 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1267 /* change to EAPD mode */
1268 {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
1269 {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
1270
1271 { }
1272};
1273
1274static struct hda_verb alc880_pin_tcl_S700_init_verbs[] = {
1275 /* Headphone output */
1276 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
1277 /* Front output*/
1278 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1279 {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
1280
1281 /* Line In pin widget for input */
1282 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1283 /* CD pin widget for input */
1284 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1285 /* Mic1 (rear panel) pin widget for input and vref at 80% */
1286 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1287
1288 /* change to EAPD mode */
1289 {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
1290 {0x20, AC_VERB_SET_PROC_COEF, 0x3070},
1291
1292 { }
1293};
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001294
1295/*
Takashi Iwaiae6b8132006-03-03 16:47:17 +01001296 * LG m1 express dual
1297 *
1298 * Pin assignment:
1299 * Rear Line-In/Out (blue): 0x14
1300 * Build-in Mic-In: 0x15
1301 * Speaker-out: 0x17
1302 * HP-Out (green): 0x1b
1303 * Mic-In/Out (red): 0x19
1304 * SPDIF-Out: 0x1e
1305 */
1306
1307/* To make 5.1 output working (green=Front, blue=Surr, red=CLFE) */
1308static hda_nid_t alc880_lg_dac_nids[3] = {
1309 0x05, 0x02, 0x03
1310};
1311
1312/* seems analog CD is not working */
1313static struct hda_input_mux alc880_lg_capture_source = {
1314 .num_items = 3,
1315 .items = {
1316 { "Mic", 0x1 },
1317 { "Line", 0x5 },
1318 { "Internal Mic", 0x6 },
1319 },
1320};
1321
1322/* 2,4,6 channel modes */
1323static struct hda_verb alc880_lg_ch2_init[] = {
1324 /* set line-in and mic-in to input */
1325 { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
1326 { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
1327 { }
1328};
1329
1330static struct hda_verb alc880_lg_ch4_init[] = {
1331 /* set line-in to out and mic-in to input */
1332 { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
1333 { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
1334 { }
1335};
1336
1337static struct hda_verb alc880_lg_ch6_init[] = {
1338 /* set line-in and mic-in to output */
1339 { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
1340 { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
1341 { }
1342};
1343
1344static struct hda_channel_mode alc880_lg_ch_modes[3] = {
1345 { 2, alc880_lg_ch2_init },
1346 { 4, alc880_lg_ch4_init },
1347 { 6, alc880_lg_ch6_init },
1348};
1349
1350static struct snd_kcontrol_new alc880_lg_mixer[] = {
1351 /* FIXME: it's not really "master" but front channels */
1352 HDA_CODEC_VOLUME("Master Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
1353 HDA_BIND_MUTE("Master Playback Switch", 0x0f, 2, HDA_INPUT),
1354 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
1355 HDA_BIND_MUTE("Surround Playback Switch", 0x0c, 2, HDA_INPUT),
1356 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
1357 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
1358 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
1359 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
1360 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
1361 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
1362 HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x06, HDA_INPUT),
1363 HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x06, HDA_INPUT),
1364 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x07, HDA_INPUT),
1365 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x07, HDA_INPUT),
1366 {
1367 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1368 .name = "Channel Mode",
1369 .info = alc_ch_mode_info,
1370 .get = alc_ch_mode_get,
1371 .put = alc_ch_mode_put,
1372 },
1373 { } /* end */
1374};
1375
1376static struct hda_verb alc880_lg_init_verbs[] = {
1377 /* set capture source to mic-in */
1378 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1379 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1380 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1381 /* mute all amp mixer inputs */
1382 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
1383 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)},
1384 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(7)},
1385 /* line-in to input */
1386 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1387 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1388 /* built-in mic */
1389 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1390 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1391 /* speaker-out */
1392 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
1393 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1394 /* mic-in to input */
1395 {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
1396 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1397 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1398 /* HP-out */
1399 {0x13, AC_VERB_SET_CONNECT_SEL, 0x03},
1400 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
1401 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1402 /* jack sense */
1403 {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | 0x1},
1404 { }
1405};
1406
1407/* toggle speaker-output according to the hp-jack state */
1408static void alc880_lg_automute(struct hda_codec *codec)
1409{
1410 unsigned int present;
1411
1412 present = snd_hda_codec_read(codec, 0x1b, 0,
1413 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1414 snd_hda_codec_amp_update(codec, 0x17, 0, HDA_OUTPUT, 0,
1415 0x80, present ? 0x80 : 0);
1416 snd_hda_codec_amp_update(codec, 0x17, 1, HDA_OUTPUT, 0,
1417 0x80, present ? 0x80 : 0);
1418}
1419
1420static void alc880_lg_unsol_event(struct hda_codec *codec, unsigned int res)
1421{
1422 /* Looks like the unsol event is incompatible with the standard
1423 * definition. 4bit tag is placed at 28 bit!
1424 */
1425 if ((res >> 28) == 0x01)
1426 alc880_lg_automute(codec);
1427}
1428
1429/*
1430 * Common callbacks
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001431 */
Takashi Iwai16ded522005-06-10 19:58:24 +02001432
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433static int alc_init(struct hda_codec *codec)
1434{
1435 struct alc_spec *spec = codec->spec;
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001436 unsigned int i;
1437
1438 for (i = 0; i < spec->num_init_verbs; i++)
1439 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwaiae6b8132006-03-03 16:47:17 +01001440
1441 if (spec->init_hook)
1442 spec->init_hook(codec);
1443
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 return 0;
1445}
1446
Takashi Iwaiae6b8132006-03-03 16:47:17 +01001447static void alc_unsol_event(struct hda_codec *codec, unsigned int res)
1448{
1449 struct alc_spec *spec = codec->spec;
1450
1451 if (spec->unsol_event)
1452 spec->unsol_event(codec, res);
1453}
1454
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455#ifdef CONFIG_PM
1456/*
1457 * resume
1458 */
1459static int alc_resume(struct hda_codec *codec)
1460{
1461 struct alc_spec *spec = codec->spec;
1462 int i;
1463
1464 alc_init(codec);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001465 for (i = 0; i < spec->num_mixers; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 snd_hda_resume_ctls(codec, spec->mixers[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 if (spec->multiout.dig_out_nid)
1468 snd_hda_resume_spdif_out(codec);
1469 if (spec->dig_in_nid)
1470 snd_hda_resume_spdif_in(codec);
1471
1472 return 0;
1473}
1474#endif
1475
1476/*
1477 * Analog playback callbacks
1478 */
1479static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo,
1480 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001481 struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482{
1483 struct alc_spec *spec = codec->spec;
1484 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
1485}
1486
1487static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
1488 struct hda_codec *codec,
1489 unsigned int stream_tag,
1490 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001491 struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492{
1493 struct alc_spec *spec = codec->spec;
1494 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1495 format, substream);
1496}
1497
1498static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1499 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001500 struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501{
1502 struct alc_spec *spec = codec->spec;
1503 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
1504}
1505
1506/*
1507 * Digital out
1508 */
1509static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1510 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001511 struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512{
1513 struct alc_spec *spec = codec->spec;
1514 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1515}
1516
1517static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1518 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001519 struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520{
1521 struct alc_spec *spec = codec->spec;
1522 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1523}
1524
1525/*
1526 * Analog capture
1527 */
1528static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1529 struct hda_codec *codec,
1530 unsigned int stream_tag,
1531 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001532 struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533{
1534 struct alc_spec *spec = codec->spec;
1535
1536 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1537 stream_tag, 0, format);
1538 return 0;
1539}
1540
1541static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1542 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001543 struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544{
1545 struct alc_spec *spec = codec->spec;
1546
1547 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
1548 return 0;
1549}
1550
1551
1552/*
1553 */
1554static struct hda_pcm_stream alc880_pcm_analog_playback = {
1555 .substreams = 1,
1556 .channels_min = 2,
1557 .channels_max = 8,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001558 /* NID is set in alc_build_pcms */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 .ops = {
1560 .open = alc880_playback_pcm_open,
1561 .prepare = alc880_playback_pcm_prepare,
1562 .cleanup = alc880_playback_pcm_cleanup
1563 },
1564};
1565
1566static struct hda_pcm_stream alc880_pcm_analog_capture = {
1567 .substreams = 2,
1568 .channels_min = 2,
1569 .channels_max = 2,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001570 /* NID is set in alc_build_pcms */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571 .ops = {
1572 .prepare = alc880_capture_pcm_prepare,
1573 .cleanup = alc880_capture_pcm_cleanup
1574 },
1575};
1576
1577static struct hda_pcm_stream alc880_pcm_digital_playback = {
1578 .substreams = 1,
1579 .channels_min = 2,
1580 .channels_max = 2,
1581 /* NID is set in alc_build_pcms */
1582 .ops = {
1583 .open = alc880_dig_playback_pcm_open,
1584 .close = alc880_dig_playback_pcm_close
1585 },
1586};
1587
1588static struct hda_pcm_stream alc880_pcm_digital_capture = {
1589 .substreams = 1,
1590 .channels_min = 2,
1591 .channels_max = 2,
1592 /* NID is set in alc_build_pcms */
1593};
1594
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01001595/* Used by alc_build_pcms to flag that a PCM has no playback stream */
1596static struct hda_pcm_stream alc_pcm_null_playback = {
1597 .substreams = 0,
1598 .channels_min = 0,
1599 .channels_max = 0,
1600};
1601
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602static int alc_build_pcms(struct hda_codec *codec)
1603{
1604 struct alc_spec *spec = codec->spec;
1605 struct hda_pcm *info = spec->pcm_rec;
1606 int i;
1607
1608 codec->num_pcms = 1;
1609 codec->pcm_info = info;
1610
1611 info->name = spec->stream_name_analog;
Takashi Iwai4a471b72005-12-07 13:56:29 +01001612 if (spec->stream_analog_playback) {
1613 snd_assert(spec->multiout.dac_nids, return -EINVAL);
1614 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
1615 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
1616 }
1617 if (spec->stream_analog_capture) {
1618 snd_assert(spec->adc_nids, return -EINVAL);
1619 info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1620 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1621 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622
Takashi Iwai4a471b72005-12-07 13:56:29 +01001623 if (spec->channel_mode) {
1624 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
1625 for (i = 0; i < spec->num_channel_mode; i++) {
1626 if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
1627 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
1628 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 }
1630 }
1631
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01001632 /* If the use of more than one ADC is requested for the current
1633 * model, configure a second analog capture-only PCM.
1634 */
1635 if (spec->num_adc_nids > 1) {
1636 codec->num_pcms++;
1637 info++;
1638 info->name = spec->stream_name_analog;
1639 /* No playback stream for second PCM */
1640 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = alc_pcm_null_playback;
1641 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
1642 if (spec->stream_analog_capture) {
1643 snd_assert(spec->adc_nids, return -EINVAL);
1644 info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1645 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[1];
1646 }
1647 }
1648
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1650 codec->num_pcms++;
1651 info++;
1652 info->name = spec->stream_name_digital;
Takashi Iwai4a471b72005-12-07 13:56:29 +01001653 if (spec->multiout.dig_out_nid &&
1654 spec->stream_digital_playback) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback);
1656 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
1657 }
Takashi Iwai4a471b72005-12-07 13:56:29 +01001658 if (spec->dig_in_nid &&
1659 spec->stream_digital_capture) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture);
1661 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
1662 }
1663 }
1664
1665 return 0;
1666}
1667
1668static void alc_free(struct hda_codec *codec)
1669{
Takashi Iwaie9edcee2005-06-13 14:16:38 +02001670 struct alc_spec *spec = codec->spec;
1671 unsigned int i;
1672
1673 if (! spec)
1674 return;
1675
1676 if (spec->kctl_alloc) {
1677 for (i = 0; i < spec->num_kctl_used; i++)
1678 kfree(spec->kctl_alloc[i].name);
1679 kfree(spec->kctl_alloc);
1680 }
1681 kfree(spec);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682}
1683
1684/*
1685 */
1686static struct hda_codec_ops alc_patch_ops = {
1687 .build_controls = alc_build_controls,
1688 .build_pcms = alc_build_pcms,
1689 .init = alc_init,
1690 .free = alc_free,
Takashi Iwaiae6b8132006-03-03 16:47:17 +01001691 .unsol_event = alc_unsol_event,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692#ifdef CONFIG_PM
1693 .resume = alc_resume,
1694#endif
1695};
1696
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001697
1698/*
1699 * Test configuration for debugging
1700 *
1701 * Almost all inputs/outputs are enabled. I/O pins can be configured via
1702 * enum controls.
1703 */
1704#ifdef CONFIG_SND_DEBUG
1705static hda_nid_t alc880_test_dac_nids[4] = {
1706 0x02, 0x03, 0x04, 0x05
1707};
1708
1709static struct hda_input_mux alc880_test_capture_source = {
Takashi Iwaiae6b8132006-03-03 16:47:17 +01001710 .num_items = 7,
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001711 .items = {
1712 { "In-1", 0x0 },
1713 { "In-2", 0x1 },
1714 { "In-3", 0x2 },
1715 { "In-4", 0x3 },
1716 { "CD", 0x4 },
Takashi Iwaiae6b8132006-03-03 16:47:17 +01001717 { "Front", 0x5 },
1718 { "Surround", 0x6 },
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001719 },
1720};
1721
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +01001722static struct hda_channel_mode alc880_test_modes[4] = {
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001723 { 2, NULL },
Takashi Iwaifd2c3262005-05-13 17:18:42 +02001724 { 4, NULL },
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001725 { 6, NULL },
Takashi Iwaifd2c3262005-05-13 17:18:42 +02001726 { 8, NULL },
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001727};
1728
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001729static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001730{
1731 static char *texts[] = {
1732 "N/A", "Line Out", "HP Out",
1733 "In Hi-Z", "In 50%", "In Grd", "In 80%", "In 100%"
1734 };
1735 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1736 uinfo->count = 1;
1737 uinfo->value.enumerated.items = 8;
1738 if (uinfo->value.enumerated.item >= 8)
1739 uinfo->value.enumerated.item = 7;
1740 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1741 return 0;
1742}
1743
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001744static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001745{
1746 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1747 hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
1748 unsigned int pin_ctl, item = 0;
1749
1750 pin_ctl = snd_hda_codec_read(codec, nid, 0,
1751 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1752 if (pin_ctl & AC_PINCTL_OUT_EN) {
1753 if (pin_ctl & AC_PINCTL_HP_EN)
1754 item = 2;
1755 else
1756 item = 1;
1757 } else if (pin_ctl & AC_PINCTL_IN_EN) {
1758 switch (pin_ctl & AC_PINCTL_VREFEN) {
1759 case AC_PINCTL_VREF_HIZ: item = 3; break;
1760 case AC_PINCTL_VREF_50: item = 4; break;
1761 case AC_PINCTL_VREF_GRD: item = 5; break;
1762 case AC_PINCTL_VREF_80: item = 6; break;
1763 case AC_PINCTL_VREF_100: item = 7; break;
1764 }
1765 }
1766 ucontrol->value.enumerated.item[0] = item;
1767 return 0;
1768}
1769
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001770static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001771{
1772 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1773 hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
1774 static unsigned int ctls[] = {
1775 0, AC_PINCTL_OUT_EN, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN,
1776 AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ,
1777 AC_PINCTL_IN_EN | AC_PINCTL_VREF_50,
1778 AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD,
1779 AC_PINCTL_IN_EN | AC_PINCTL_VREF_80,
1780 AC_PINCTL_IN_EN | AC_PINCTL_VREF_100,
1781 };
1782 unsigned int old_ctl, new_ctl;
1783
1784 old_ctl = snd_hda_codec_read(codec, nid, 0,
1785 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1786 new_ctl = ctls[ucontrol->value.enumerated.item[0]];
1787 if (old_ctl != new_ctl) {
1788 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl);
1789 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
1790 ucontrol->value.enumerated.item[0] >= 3 ? 0xb080 : 0xb000);
1791 return 1;
1792 }
1793 return 0;
1794}
1795
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001796static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001797{
1798 static char *texts[] = {
1799 "Front", "Surround", "CLFE", "Side"
1800 };
1801 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1802 uinfo->count = 1;
1803 uinfo->value.enumerated.items = 4;
1804 if (uinfo->value.enumerated.item >= 4)
1805 uinfo->value.enumerated.item = 3;
1806 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1807 return 0;
1808}
1809
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001810static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001811{
1812 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1813 hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
1814 unsigned int sel;
1815
1816 sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0);
1817 ucontrol->value.enumerated.item[0] = sel & 3;
1818 return 0;
1819}
1820
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001821static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001822{
1823 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1824 hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
1825 unsigned int sel;
1826
1827 sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0) & 3;
1828 if (ucontrol->value.enumerated.item[0] != sel) {
1829 sel = ucontrol->value.enumerated.item[0] & 3;
1830 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, sel);
1831 return 1;
1832 }
1833 return 0;
1834}
1835
1836#define PIN_CTL_TEST(xname,nid) { \
1837 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
1838 .name = xname, \
1839 .info = alc_test_pin_ctl_info, \
1840 .get = alc_test_pin_ctl_get, \
1841 .put = alc_test_pin_ctl_put, \
1842 .private_value = nid \
1843 }
1844
1845#define PIN_SRC_TEST(xname,nid) { \
1846 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
1847 .name = xname, \
1848 .info = alc_test_pin_src_info, \
1849 .get = alc_test_pin_src_get, \
1850 .put = alc_test_pin_src_put, \
1851 .private_value = nid \
1852 }
1853
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001854static struct snd_kcontrol_new alc880_test_mixer[] = {
Takashi Iwai05acb862005-06-10 19:50:25 +02001855 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
1856 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
1857 HDA_CODEC_VOLUME("CLFE Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
1858 HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01001859 HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
1860 HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
1861 HDA_BIND_MUTE("CLFE Playback Switch", 0x0e, 2, HDA_INPUT),
1862 HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001863 PIN_CTL_TEST("Front Pin Mode", 0x14),
1864 PIN_CTL_TEST("Surround Pin Mode", 0x15),
1865 PIN_CTL_TEST("CLFE Pin Mode", 0x16),
1866 PIN_CTL_TEST("Side Pin Mode", 0x17),
1867 PIN_CTL_TEST("In-1 Pin Mode", 0x18),
1868 PIN_CTL_TEST("In-2 Pin Mode", 0x19),
1869 PIN_CTL_TEST("In-3 Pin Mode", 0x1a),
1870 PIN_CTL_TEST("In-4 Pin Mode", 0x1b),
1871 PIN_SRC_TEST("In-1 Pin Source", 0x18),
1872 PIN_SRC_TEST("In-2 Pin Source", 0x19),
1873 PIN_SRC_TEST("In-3 Pin Source", 0x1a),
1874 PIN_SRC_TEST("In-4 Pin Source", 0x1b),
1875 HDA_CODEC_VOLUME("In-1 Playback Volume", 0x0b, 0x0, HDA_INPUT),
1876 HDA_CODEC_MUTE("In-1 Playback Switch", 0x0b, 0x0, HDA_INPUT),
1877 HDA_CODEC_VOLUME("In-2 Playback Volume", 0x0b, 0x1, HDA_INPUT),
1878 HDA_CODEC_MUTE("In-2 Playback Switch", 0x0b, 0x1, HDA_INPUT),
1879 HDA_CODEC_VOLUME("In-3 Playback Volume", 0x0b, 0x2, HDA_INPUT),
1880 HDA_CODEC_MUTE("In-3 Playback Switch", 0x0b, 0x2, HDA_INPUT),
1881 HDA_CODEC_VOLUME("In-4 Playback Volume", 0x0b, 0x3, HDA_INPUT),
1882 HDA_CODEC_MUTE("In-4 Playback Switch", 0x0b, 0x3, HDA_INPUT),
1883 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x4, HDA_INPUT),
1884 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x4, HDA_INPUT),
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001885 {
1886 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1887 .name = "Channel Mode",
Kailang Yangdf694da2005-12-05 19:42:22 +01001888 .info = alc_ch_mode_info,
1889 .get = alc_ch_mode_get,
1890 .put = alc_ch_mode_put,
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001891 },
1892 { } /* end */
1893};
1894
1895static struct hda_verb alc880_test_init_verbs[] = {
1896 /* Unmute inputs of 0x0c - 0x0f */
Takashi Iwai05acb862005-06-10 19:50:25 +02001897 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1898 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1899 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1900 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1901 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1902 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1903 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1904 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001905 /* Vol output for 0x0c-0x0f */
Takashi Iwai05acb862005-06-10 19:50:25 +02001906 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1907 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1908 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1909 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001910 /* Set output pins 0x14-0x17 */
Takashi Iwai05acb862005-06-10 19:50:25 +02001911 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1912 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1913 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1914 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001915 /* Unmute output pins 0x14-0x17 */
Takashi Iwai05acb862005-06-10 19:50:25 +02001916 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1917 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1918 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1919 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001920 /* Set input pins 0x18-0x1c */
Takashi Iwai16ded522005-06-10 19:58:24 +02001921 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1922 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai05acb862005-06-10 19:50:25 +02001923 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1924 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1925 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001926 /* Mute input pins 0x18-0x1b */
Takashi Iwai05acb862005-06-10 19:50:25 +02001927 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1928 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1929 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
1930 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Takashi Iwai71fe7b82005-05-25 18:11:40 +02001931 /* ADC set up */
Takashi Iwai05acb862005-06-10 19:50:25 +02001932 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +02001933 {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwai05acb862005-06-10 19:50:25 +02001934 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +02001935 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwai05acb862005-06-10 19:50:25 +02001936 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +02001937 {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwai05acb862005-06-10 19:50:25 +02001938 /* Analog input/passthru */
1939 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
1940 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
1941 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
1942 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
1943 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwai2fa522b2005-05-12 14:51:12 +02001944 { }
1945};
1946#endif
1947
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948/*
1949 */
1950
1951static struct hda_board_config alc880_cfg_tbl[] = {
1952 /* Back 3 jack, front 2 jack */
1953 { .modelname = "3stack", .config = ALC880_3ST },
Takashi Iwai72915482005-05-12 16:49:45 +02001954 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe200, .config = ALC880_3ST },
1955 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe201, .config = ALC880_3ST },
1956 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe202, .config = ALC880_3ST },
1957 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe203, .config = ALC880_3ST },
1958 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe204, .config = ALC880_3ST },
1959 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe205, .config = ALC880_3ST },
1960 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe206, .config = ALC880_3ST },
1961 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe207, .config = ALC880_3ST },
1962 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe208, .config = ALC880_3ST },
1963 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe209, .config = ALC880_3ST },
1964 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20a, .config = ALC880_3ST },
1965 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20b, .config = ALC880_3ST },
1966 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20c, .config = ALC880_3ST },
1967 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20d, .config = ALC880_3ST },
1968 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20e, .config = ALC880_3ST },
1969 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20f, .config = ALC880_3ST },
1970 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe210, .config = ALC880_3ST },
1971 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe211, .config = ALC880_3ST },
1972 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe214, .config = ALC880_3ST },
1973 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe302, .config = ALC880_3ST },
1974 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe303, .config = ALC880_3ST },
1975 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe304, .config = ALC880_3ST },
1976 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe306, .config = ALC880_3ST },
1977 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe307, .config = ALC880_3ST },
1978 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe404, .config = ALC880_3ST },
1979 { .pci_subvendor = 0x8086, .pci_subdevice = 0xa101, .config = ALC880_3ST },
1980 { .pci_subvendor = 0x107b, .pci_subdevice = 0x3031, .config = ALC880_3ST },
1981 { .pci_subvendor = 0x107b, .pci_subdevice = 0x4036, .config = ALC880_3ST },
1982 { .pci_subvendor = 0x107b, .pci_subdevice = 0x4037, .config = ALC880_3ST },
1983 { .pci_subvendor = 0x107b, .pci_subdevice = 0x4038, .config = ALC880_3ST },
1984 { .pci_subvendor = 0x107b, .pci_subdevice = 0x4040, .config = ALC880_3ST },
1985 { .pci_subvendor = 0x107b, .pci_subdevice = 0x4041, .config = ALC880_3ST },
Kailang Yangdf694da2005-12-05 19:42:22 +01001986 /* TCL S700 */
1987 { .pci_subvendor = 0x19db, .pci_subdevice = 0x4188, .config = ALC880_TCL_S700 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988
1989 /* Back 3 jack, front 2 jack (Internal add Aux-In) */
Takashi Iwai72915482005-05-12 16:49:45 +02001990 { .pci_subvendor = 0x1025, .pci_subdevice = 0xe310, .config = ALC880_3ST },
Takashi Iwai16ded522005-06-10 19:58:24 +02001991 { .pci_subvendor = 0x104d, .pci_subdevice = 0x81d6, .config = ALC880_3ST },
Davide Libenzi0ca21612005-09-05 11:56:47 +02001992 { .pci_subvendor = 0x104d, .pci_subdevice = 0x81a0, .config = ALC880_3ST },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993
1994 /* Back 3 jack plus 1 SPDIF out jack, front 2 jack */
1995 { .modelname = "3stack-digout", .config = ALC880_3ST_DIG },
Takashi Iwai72915482005-05-12 16:49:45 +02001996 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe308, .config = ALC880_3ST_DIG },
Jaroslav Kysela5a47fe3c2005-08-15 20:01:40 +02001997 { .pci_subvendor = 0x1025, .pci_subdevice = 0x0070, .config = ALC880_3ST_DIG },
Kailang Yangdf694da2005-12-05 19:42:22 +01001998 /* Clevo m520G NB */
1999 { .pci_subvendor = 0x1558, .pci_subdevice = 0x0520, .config = ALC880_CLEVO },
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000
2001 /* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/
Takashi Iwai72915482005-05-12 16:49:45 +02002002 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe305, .config = ALC880_3ST_DIG },
2003 { .pci_subvendor = 0x8086, .pci_subdevice = 0xd402, .config = ALC880_3ST_DIG },
2004 { .pci_subvendor = 0x1025, .pci_subdevice = 0xe309, .config = ALC880_3ST_DIG },
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005
2006 /* Back 5 jack, front 2 jack */
2007 { .modelname = "5stack", .config = ALC880_5ST },
Takashi Iwai72915482005-05-12 16:49:45 +02002008 { .pci_subvendor = 0x107b, .pci_subdevice = 0x3033, .config = ALC880_5ST },
2009 { .pci_subvendor = 0x107b, .pci_subdevice = 0x4039, .config = ALC880_5ST },
2010 { .pci_subvendor = 0x107b, .pci_subdevice = 0x3032, .config = ALC880_5ST },
2011 { .pci_subvendor = 0x103c, .pci_subdevice = 0x2a09, .config = ALC880_5ST },
Takashi Iwai16ded522005-06-10 19:58:24 +02002012 { .pci_subvendor = 0x1043, .pci_subdevice = 0x814e, .config = ALC880_5ST },
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013
2014 /* Back 5 jack plus 1 SPDIF out jack, front 2 jack */
2015 { .modelname = "5stack-digout", .config = ALC880_5ST_DIG },
Takashi Iwai72915482005-05-12 16:49:45 +02002016 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe224, .config = ALC880_5ST_DIG },
2017 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe400, .config = ALC880_5ST_DIG },
2018 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe401, .config = ALC880_5ST_DIG },
2019 { .pci_subvendor = 0x8086, .pci_subdevice = 0xe402, .config = ALC880_5ST_DIG },
2020 { .pci_subvendor = 0x8086, .pci_subdevice = 0xd400, .config = ALC880_5ST_DIG },
2021 { .pci_subvendor = 0x8086, .pci_subdevice = 0xd401, .config = ALC880_5ST_DIG },
2022 { .pci_subvendor = 0x8086, .pci_subdevice = 0xa100, .config = ALC880_5ST_DIG },
2023 { .pci_subvendor = 0x1565, .pci_subdevice = 0x8202, .config = ALC880_5ST_DIG },
Takashi Iwai16ded522005-06-10 19:58:24 +02002024 { .pci_subvendor = 0x1019, .pci_subdevice = 0xa880, .config = ALC880_5ST_DIG },
Takashi Iwaiede35312006-03-13 14:21:16 +01002025 { .pci_subvendor = 0xa0a0, .pci_subdevice = 0x0560,
2026 .config = ALC880_5ST_DIG }, /* Aopen i915GMm-HFS */
Takashi Iwai7a318a72005-06-28 14:16:21 +02002027 /* { .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_5ST_DIG }, */ /* conflict with 6stack */
Takashi Iwai16ded522005-06-10 19:58:24 +02002028 { .pci_subvendor = 0x1695, .pci_subdevice = 0x400d, .config = ALC880_5ST_DIG },
Takashi Iwaib0af0de2005-06-21 14:49:19 +02002029 /* note subvendor = 0 below */
2030 /* { .pci_subvendor = 0x0000, .pci_subdevice = 0x8086, .config = ALC880_5ST_DIG }, */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031
2032 { .modelname = "w810", .config = ALC880_W810 },
Takashi Iwai72915482005-05-12 16:49:45 +02002033 { .pci_subvendor = 0x161f, .pci_subdevice = 0x203d, .config = ALC880_W810 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034
Takashi Iwaidfc0ff62005-05-12 14:31:49 +02002035 { .modelname = "z71v", .config = ALC880_Z71V },
Takashi Iwai72915482005-05-12 16:49:45 +02002036 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_Z71V },
Takashi Iwaidfc0ff62005-05-12 14:31:49 +02002037
Takashi Iwaib6482d42005-06-27 15:32:43 +02002038 { .modelname = "6stack", .config = ALC880_6ST },
Takashi Iwai7632c7b2005-12-07 18:25:47 +01002039 { .pci_subvendor = 0x1043, .pci_subdevice = 0x8196, .config = ALC880_6ST }, /* ASUS P5GD1-HVM */
Kailang Yangdf694da2005-12-05 19:42:22 +01002040 { .pci_subvendor = 0x1043, .pci_subdevice = 0x81b4, .config = ALC880_6ST },
Takashi Iwai7a318a72005-06-28 14:16:21 +02002041 { .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_6ST }, /* Acer APFV */
Arnaud Patardbae2bdb2006-01-27 12:05:02 +01002042 { .pci_subvendor = 0x1458, .pci_subdevice = 0xa102, .config = ALC880_6ST }, /* Gigabyte K8N51 */
Takashi Iwaib6482d42005-06-27 15:32:43 +02002043
2044 { .modelname = "6stack-digout", .config = ALC880_6ST_DIG },
Takashi Iwai16ded522005-06-10 19:58:24 +02002045 { .pci_subvendor = 0x2668, .pci_subdevice = 0x8086, .config = ALC880_6ST_DIG },
2046 { .pci_subvendor = 0x8086, .pci_subdevice = 0x2668, .config = ALC880_6ST_DIG },
2047 { .pci_subvendor = 0x1462, .pci_subdevice = 0x1150, .config = ALC880_6ST_DIG },
2048 { .pci_subvendor = 0xe803, .pci_subdevice = 0x1019, .config = ALC880_6ST_DIG },
Kailang Yangdf694da2005-12-05 19:42:22 +01002049 { .pci_subvendor = 0x1039, .pci_subdevice = 0x1234, .config = ALC880_6ST_DIG },
2050 { .pci_subvendor = 0x1025, .pci_subdevice = 0x0077, .config = ALC880_6ST_DIG },
2051 { .pci_subvendor = 0x1025, .pci_subdevice = 0x0078, .config = ALC880_6ST_DIG },
2052 { .pci_subvendor = 0x1025, .pci_subdevice = 0x0087, .config = ALC880_6ST_DIG },
Takashi Iwai041dec02005-12-23 12:27:52 +01002053 { .pci_subvendor = 0x1297, .pci_subdevice = 0xc790, .config = ALC880_6ST_DIG }, /* Shuttle ST20G5 */
Takashi Iwaia12606c2006-03-15 18:24:43 +01002054 { .pci_subvendor = 0x1509, .pci_subdevice = 0x925d, .config = ALC880_6ST_DIG }, /* FIC P4M-915GD1 */
Takashi Iwai16ded522005-06-10 19:58:24 +02002055
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002056 { .modelname = "asus", .config = ALC880_ASUS },
Takashi Iwai16ded522005-06-10 19:58:24 +02002057 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_ASUS_DIG },
2058 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1973, .config = ALC880_ASUS_DIG },
2059 { .pci_subvendor = 0x1043, .pci_subdevice = 0x19b3, .config = ALC880_ASUS_DIG },
2060 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1113, .config = ALC880_ASUS_DIG },
Takashi Iwai86488112005-09-09 13:56:32 +02002061 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1173, .config = ALC880_ASUS_DIG },
Takashi Iwai16ded522005-06-10 19:58:24 +02002062 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1993, .config = ALC880_ASUS },
2063 { .pci_subvendor = 0x1043, .pci_subdevice = 0x10c3, .config = ALC880_ASUS_DIG },
2064 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1133, .config = ALC880_ASUS },
2065 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1123, .config = ALC880_ASUS_DIG },
2066 { .pci_subvendor = 0x1043, .pci_subdevice = 0x1143, .config = ALC880_ASUS },
2067 { .pci_subvendor = 0x1043, .pci_subdevice = 0x10b3, .config = ALC880_ASUS_W1V },
Takashi Iwai84f34302006-03-21 19:12:53 +01002068 { .pci_subvendor = 0x1043, .pci_subdevice = 0x8181, .config = ALC880_ASUS_DIG }, /* ASUS P4GPL-X */
Kailang Yangdf694da2005-12-05 19:42:22 +01002069 { .pci_subvendor = 0x1558, .pci_subdevice = 0x5401, .config = ALC880_ASUS_DIG2 },
Takashi Iwai16ded522005-06-10 19:58:24 +02002070
2071 { .modelname = "uniwill", .config = ALC880_UNIWILL_DIG },
2072 { .pci_subvendor = 0x1584, .pci_subdevice = 0x9050, .config = ALC880_UNIWILL_DIG },
2073
2074 { .modelname = "F1734", .config = ALC880_F1734 },
2075 { .pci_subvendor = 0x1734, .pci_subdevice = 0x107c, .config = ALC880_F1734 },
Kailang Yangdf694da2005-12-05 19:42:22 +01002076 { .pci_subvendor = 0x1584, .pci_subdevice = 0x9054, .config = ALC880_F1734 },
Takashi Iwai16ded522005-06-10 19:58:24 +02002077
Takashi Iwaiae6b8132006-03-03 16:47:17 +01002078 { .modelname = "lg", .config = ALC880_LG },
2079 { .pci_subvendor = 0x1854, .pci_subdevice = 0x003b, .config = ALC880_LG },
2080
Takashi Iwai2fa522b2005-05-12 14:51:12 +02002081#ifdef CONFIG_SND_DEBUG
2082 { .modelname = "test", .config = ALC880_TEST },
2083#endif
Kailang Yangdf694da2005-12-05 19:42:22 +01002084 { .modelname = "auto", .config = ALC880_AUTO },
Takashi Iwai2fa522b2005-05-12 14:51:12 +02002085
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 {}
2087};
2088
Takashi Iwai16ded522005-06-10 19:58:24 +02002089/*
Kailang Yangdf694da2005-12-05 19:42:22 +01002090 * ALC880 codec presets
Takashi Iwai16ded522005-06-10 19:58:24 +02002091 */
Takashi Iwai16ded522005-06-10 19:58:24 +02002092static struct alc_config_preset alc880_presets[] = {
2093 [ALC880_3ST] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002094 .mixers = { alc880_three_stack_mixer },
2095 .init_verbs = { alc880_volume_init_verbs, alc880_pin_3stack_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02002096 .num_dacs = ARRAY_SIZE(alc880_dac_nids),
Takashi Iwai16ded522005-06-10 19:58:24 +02002097 .dac_nids = alc880_dac_nids,
Takashi Iwai16ded522005-06-10 19:58:24 +02002098 .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
2099 .channel_mode = alc880_threestack_modes,
2100 .input_mux = &alc880_capture_source,
2101 },
2102 [ALC880_3ST_DIG] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002103 .mixers = { alc880_three_stack_mixer },
2104 .init_verbs = { alc880_volume_init_verbs, alc880_pin_3stack_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02002105 .num_dacs = ARRAY_SIZE(alc880_dac_nids),
Takashi Iwai16ded522005-06-10 19:58:24 +02002106 .dac_nids = alc880_dac_nids,
2107 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwai16ded522005-06-10 19:58:24 +02002108 .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
2109 .channel_mode = alc880_threestack_modes,
2110 .input_mux = &alc880_capture_source,
2111 },
Kailang Yangdf694da2005-12-05 19:42:22 +01002112 [ALC880_TCL_S700] = {
2113 .mixers = { alc880_tcl_s700_mixer },
2114 .init_verbs = { alc880_volume_init_verbs,
2115 alc880_pin_tcl_S700_init_verbs,
2116 alc880_gpio2_init_verbs },
2117 .num_dacs = ARRAY_SIZE(alc880_dac_nids),
2118 .dac_nids = alc880_dac_nids,
2119 .hp_nid = 0x03,
2120 .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
2121 .channel_mode = alc880_2_jack_modes,
2122 .input_mux = &alc880_capture_source,
2123 },
Takashi Iwai16ded522005-06-10 19:58:24 +02002124 [ALC880_5ST] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002125 .mixers = { alc880_three_stack_mixer, alc880_five_stack_mixer},
2126 .init_verbs = { alc880_volume_init_verbs, alc880_pin_5stack_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02002127 .num_dacs = ARRAY_SIZE(alc880_dac_nids),
2128 .dac_nids = alc880_dac_nids,
Takashi Iwai16ded522005-06-10 19:58:24 +02002129 .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes),
2130 .channel_mode = alc880_fivestack_modes,
2131 .input_mux = &alc880_capture_source,
2132 },
2133 [ALC880_5ST_DIG] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002134 .mixers = { alc880_three_stack_mixer, alc880_five_stack_mixer },
2135 .init_verbs = { alc880_volume_init_verbs, alc880_pin_5stack_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02002136 .num_dacs = ARRAY_SIZE(alc880_dac_nids),
2137 .dac_nids = alc880_dac_nids,
2138 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwai16ded522005-06-10 19:58:24 +02002139 .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes),
2140 .channel_mode = alc880_fivestack_modes,
2141 .input_mux = &alc880_capture_source,
2142 },
Takashi Iwaib6482d42005-06-27 15:32:43 +02002143 [ALC880_6ST] = {
2144 .mixers = { alc880_six_stack_mixer },
2145 .init_verbs = { alc880_volume_init_verbs, alc880_pin_6stack_init_verbs },
2146 .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids),
2147 .dac_nids = alc880_6st_dac_nids,
2148 .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes),
2149 .channel_mode = alc880_sixstack_modes,
2150 .input_mux = &alc880_6stack_capture_source,
2151 },
Takashi Iwai16ded522005-06-10 19:58:24 +02002152 [ALC880_6ST_DIG] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002153 .mixers = { alc880_six_stack_mixer },
2154 .init_verbs = { alc880_volume_init_verbs, alc880_pin_6stack_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02002155 .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids),
2156 .dac_nids = alc880_6st_dac_nids,
2157 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwai16ded522005-06-10 19:58:24 +02002158 .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes),
2159 .channel_mode = alc880_sixstack_modes,
2160 .input_mux = &alc880_6stack_capture_source,
2161 },
2162 [ALC880_W810] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002163 .mixers = { alc880_w810_base_mixer },
Takashi Iwaib0af0de2005-06-21 14:49:19 +02002164 .init_verbs = { alc880_volume_init_verbs, alc880_pin_w810_init_verbs,
2165 alc880_gpio2_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02002166 .num_dacs = ARRAY_SIZE(alc880_w810_dac_nids),
2167 .dac_nids = alc880_w810_dac_nids,
2168 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwai16ded522005-06-10 19:58:24 +02002169 .num_channel_mode = ARRAY_SIZE(alc880_w810_modes),
2170 .channel_mode = alc880_w810_modes,
2171 .input_mux = &alc880_capture_source,
2172 },
2173 [ALC880_Z71V] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002174 .mixers = { alc880_z71v_mixer },
Takashi Iwaib0af0de2005-06-21 14:49:19 +02002175 .init_verbs = { alc880_volume_init_verbs, alc880_pin_z71v_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02002176 .num_dacs = ARRAY_SIZE(alc880_z71v_dac_nids),
2177 .dac_nids = alc880_z71v_dac_nids,
2178 .dig_out_nid = ALC880_DIGOUT_NID,
2179 .hp_nid = 0x03,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002180 .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
2181 .channel_mode = alc880_2_jack_modes,
Takashi Iwai16ded522005-06-10 19:58:24 +02002182 .input_mux = &alc880_capture_source,
2183 },
2184 [ALC880_F1734] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002185 .mixers = { alc880_f1734_mixer },
2186 .init_verbs = { alc880_volume_init_verbs, alc880_pin_f1734_init_verbs },
2187 .num_dacs = ARRAY_SIZE(alc880_f1734_dac_nids),
2188 .dac_nids = alc880_f1734_dac_nids,
2189 .hp_nid = 0x02,
2190 .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
2191 .channel_mode = alc880_2_jack_modes,
Takashi Iwai16ded522005-06-10 19:58:24 +02002192 .input_mux = &alc880_capture_source,
2193 },
2194 [ALC880_ASUS] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002195 .mixers = { alc880_asus_mixer },
2196 .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
2197 alc880_gpio1_init_verbs },
2198 .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
2199 .dac_nids = alc880_asus_dac_nids,
2200 .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
2201 .channel_mode = alc880_asus_modes,
Takashi Iwai16ded522005-06-10 19:58:24 +02002202 .input_mux = &alc880_capture_source,
2203 },
2204 [ALC880_ASUS_DIG] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002205 .mixers = { alc880_asus_mixer },
2206 .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
2207 alc880_gpio1_init_verbs },
2208 .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
2209 .dac_nids = alc880_asus_dac_nids,
Takashi Iwai16ded522005-06-10 19:58:24 +02002210 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002211 .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
2212 .channel_mode = alc880_asus_modes,
Takashi Iwai16ded522005-06-10 19:58:24 +02002213 .input_mux = &alc880_capture_source,
2214 },
Kailang Yangdf694da2005-12-05 19:42:22 +01002215 [ALC880_ASUS_DIG2] = {
2216 .mixers = { alc880_asus_mixer },
2217 .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
2218 alc880_gpio2_init_verbs }, /* use GPIO2 */
2219 .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
2220 .dac_nids = alc880_asus_dac_nids,
2221 .dig_out_nid = ALC880_DIGOUT_NID,
2222 .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
2223 .channel_mode = alc880_asus_modes,
2224 .input_mux = &alc880_capture_source,
2225 },
Takashi Iwai16ded522005-06-10 19:58:24 +02002226 [ALC880_ASUS_W1V] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002227 .mixers = { alc880_asus_mixer, alc880_asus_w1v_mixer },
2228 .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
2229 alc880_gpio1_init_verbs },
2230 .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
2231 .dac_nids = alc880_asus_dac_nids,
Takashi Iwai16ded522005-06-10 19:58:24 +02002232 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002233 .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
2234 .channel_mode = alc880_asus_modes,
Takashi Iwai16ded522005-06-10 19:58:24 +02002235 .input_mux = &alc880_capture_source,
2236 },
2237 [ALC880_UNIWILL_DIG] = {
Takashi Iwai3c10a9d2005-08-23 20:02:27 +02002238 .mixers = { alc880_asus_mixer, alc880_pcbeep_mixer },
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002239 .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs },
2240 .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
2241 .dac_nids = alc880_asus_dac_nids,
Takashi Iwai16ded522005-06-10 19:58:24 +02002242 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002243 .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
2244 .channel_mode = alc880_asus_modes,
Takashi Iwai16ded522005-06-10 19:58:24 +02002245 .input_mux = &alc880_capture_source,
2246 },
Kailang Yangdf694da2005-12-05 19:42:22 +01002247 [ALC880_CLEVO] = {
2248 .mixers = { alc880_three_stack_mixer },
2249 .init_verbs = { alc880_volume_init_verbs,
2250 alc880_pin_clevo_init_verbs },
2251 .num_dacs = ARRAY_SIZE(alc880_dac_nids),
2252 .dac_nids = alc880_dac_nids,
2253 .hp_nid = 0x03,
2254 .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
2255 .channel_mode = alc880_threestack_modes,
2256 .input_mux = &alc880_capture_source,
2257 },
Takashi Iwaiae6b8132006-03-03 16:47:17 +01002258 [ALC880_LG] = {
2259 .mixers = { alc880_lg_mixer },
2260 .init_verbs = { alc880_volume_init_verbs,
2261 alc880_lg_init_verbs },
2262 .num_dacs = ARRAY_SIZE(alc880_lg_dac_nids),
2263 .dac_nids = alc880_lg_dac_nids,
2264 .dig_out_nid = ALC880_DIGOUT_NID,
2265 .num_channel_mode = ARRAY_SIZE(alc880_lg_ch_modes),
2266 .channel_mode = alc880_lg_ch_modes,
2267 .input_mux = &alc880_lg_capture_source,
2268 .unsol_event = alc880_lg_unsol_event,
2269 .init_hook = alc880_lg_automute,
2270 },
Takashi Iwai16ded522005-06-10 19:58:24 +02002271#ifdef CONFIG_SND_DEBUG
2272 [ALC880_TEST] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002273 .mixers = { alc880_test_mixer },
2274 .init_verbs = { alc880_test_init_verbs },
Takashi Iwai16ded522005-06-10 19:58:24 +02002275 .num_dacs = ARRAY_SIZE(alc880_test_dac_nids),
2276 .dac_nids = alc880_test_dac_nids,
2277 .dig_out_nid = ALC880_DIGOUT_NID,
Takashi Iwai16ded522005-06-10 19:58:24 +02002278 .num_channel_mode = ARRAY_SIZE(alc880_test_modes),
2279 .channel_mode = alc880_test_modes,
2280 .input_mux = &alc880_test_capture_source,
2281 },
2282#endif
2283};
2284
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002285/*
2286 * Automatic parse of I/O pins from the BIOS configuration
2287 */
2288
2289#define NUM_CONTROL_ALLOC 32
2290#define NUM_VERB_ALLOC 32
2291
2292enum {
2293 ALC_CTL_WIDGET_VOL,
2294 ALC_CTL_WIDGET_MUTE,
2295 ALC_CTL_BIND_MUTE,
2296};
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01002297static struct snd_kcontrol_new alc880_control_templates[] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002298 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2299 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Takashi Iwai985be542005-11-02 18:26:49 +01002300 HDA_BIND_MUTE(NULL, 0, 0, 0),
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002301};
2302
2303/* add dynamic controls */
2304static int add_control(struct alc_spec *spec, int type, const char *name, unsigned long val)
2305{
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01002306 struct snd_kcontrol_new *knew;
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002307
2308 if (spec->num_kctl_used >= spec->num_kctl_alloc) {
2309 int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
2310
2311 knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
2312 if (! knew)
2313 return -ENOMEM;
2314 if (spec->kctl_alloc) {
2315 memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
2316 kfree(spec->kctl_alloc);
2317 }
2318 spec->kctl_alloc = knew;
2319 spec->num_kctl_alloc = num;
2320 }
2321
2322 knew = &spec->kctl_alloc[spec->num_kctl_used];
2323 *knew = alc880_control_templates[type];
Paulo Marques543537b2005-06-23 00:09:02 -07002324 knew->name = kstrdup(name, GFP_KERNEL);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002325 if (! knew->name)
2326 return -ENOMEM;
2327 knew->private_value = val;
2328 spec->num_kctl_used++;
2329 return 0;
2330}
2331
2332#define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17)
2333#define alc880_fixed_pin_idx(nid) ((nid) - 0x14)
2334#define alc880_is_multi_pin(nid) ((nid) >= 0x18)
2335#define alc880_multi_pin_idx(nid) ((nid) - 0x18)
2336#define alc880_is_input_pin(nid) ((nid) >= 0x18)
2337#define alc880_input_pin_idx(nid) ((nid) - 0x18)
2338#define alc880_idx_to_dac(nid) ((nid) + 0x02)
2339#define alc880_dac_to_idx(nid) ((nid) - 0x02)
2340#define alc880_idx_to_mixer(nid) ((nid) + 0x0c)
2341#define alc880_idx_to_selector(nid) ((nid) + 0x10)
2342#define ALC880_PIN_CD_NID 0x1c
2343
2344/* fill in the dac_nids table from the parsed pin configuration */
2345static int alc880_auto_fill_dac_nids(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
2346{
2347 hda_nid_t nid;
2348 int assigned[4];
2349 int i, j;
2350
2351 memset(assigned, 0, sizeof(assigned));
Takashi Iwaib0af0de2005-06-21 14:49:19 +02002352 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002353
2354 /* check the pins hardwired to audio widget */
2355 for (i = 0; i < cfg->line_outs; i++) {
2356 nid = cfg->line_out_pins[i];
2357 if (alc880_is_fixed_pin(nid)) {
2358 int idx = alc880_fixed_pin_idx(nid);
Libin Yang5014f192005-11-23 15:48:36 +01002359 spec->multiout.dac_nids[i] = alc880_idx_to_dac(idx);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002360 assigned[idx] = 1;
2361 }
2362 }
2363 /* left pins can be connect to any audio widget */
2364 for (i = 0; i < cfg->line_outs; i++) {
2365 nid = cfg->line_out_pins[i];
2366 if (alc880_is_fixed_pin(nid))
2367 continue;
2368 /* search for an empty channel */
2369 for (j = 0; j < cfg->line_outs; j++) {
2370 if (! assigned[j]) {
2371 spec->multiout.dac_nids[i] = alc880_idx_to_dac(j);
2372 assigned[j] = 1;
2373 break;
2374 }
2375 }
2376 }
2377 spec->multiout.num_dacs = cfg->line_outs;
2378 return 0;
2379}
2380
2381/* add playback controls from the parsed DAC table */
Kailang Yangdf694da2005-12-05 19:42:22 +01002382static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
2383 const struct auto_pin_cfg *cfg)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002384{
2385 char name[32];
2386 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2387 hda_nid_t nid;
2388 int i, err;
2389
2390 for (i = 0; i < cfg->line_outs; i++) {
2391 if (! spec->multiout.dac_nids[i])
2392 continue;
2393 nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i]));
2394 if (i == 2) {
2395 /* Center/LFE */
2396 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Center Playback Volume",
2397 HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0)
2398 return err;
2399 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "LFE Playback Volume",
2400 HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
2401 return err;
2402 if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "Center Playback Switch",
2403 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT))) < 0)
2404 return err;
2405 if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "LFE Playback Switch",
2406 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT))) < 0)
2407 return err;
2408 } else {
2409 sprintf(name, "%s Playback Volume", chname[i]);
2410 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
2411 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
2412 return err;
2413 sprintf(name, "%s Playback Switch", chname[i]);
2414 if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name,
2415 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2416 return err;
2417 }
2418 }
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002419 return 0;
2420}
2421
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002422/* add playback controls for speaker and HP outputs */
2423static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
2424 const char *pfx)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002425{
2426 hda_nid_t nid;
2427 int err;
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002428 char name[32];
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002429
2430 if (! pin)
2431 return 0;
2432
2433 if (alc880_is_fixed_pin(pin)) {
2434 nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
Takashi Iwai82bc9552006-03-21 11:24:42 +01002435 /* specify the DAC as the extra output */
2436 if (! spec->multiout.hp_nid)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002437 spec->multiout.hp_nid = nid;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002438 else
2439 spec->multiout.extra_out_nid[0] = nid;
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002440 /* control HP volume/switch on the output mixer amp */
2441 nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin));
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002442 sprintf(name, "%s Playback Volume", pfx);
2443 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002444 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
2445 return err;
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002446 sprintf(name, "%s Playback Switch", pfx);
2447 if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002448 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2449 return err;
2450 } else if (alc880_is_multi_pin(pin)) {
2451 /* set manual connection */
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002452 /* we have only a switch on HP-out PIN */
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002453 sprintf(name, "%s Playback Switch", pfx);
2454 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002455 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT))) < 0)
2456 return err;
2457 }
2458 return 0;
2459}
2460
2461/* create input playback/capture controls for the given pin */
Kailang Yangdf694da2005-12-05 19:42:22 +01002462static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, const char *ctlname,
2463 int idx, hda_nid_t mix_nid)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002464{
2465 char name[32];
Kailang Yangdf694da2005-12-05 19:42:22 +01002466 int err;
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002467
2468 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002469 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
Kailang Yangdf694da2005-12-05 19:42:22 +01002470 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT))) < 0)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002471 return err;
2472 sprintf(name, "%s Playback Switch", ctlname);
2473 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
Kailang Yangdf694da2005-12-05 19:42:22 +01002474 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT))) < 0)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002475 return err;
2476 return 0;
2477}
2478
2479/* create playback/capture controls for input pins */
Kailang Yangdf694da2005-12-05 19:42:22 +01002480static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec,
2481 const struct auto_pin_cfg *cfg)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002482{
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002483 struct hda_input_mux *imux = &spec->private_imux;
Kailang Yangdf694da2005-12-05 19:42:22 +01002484 int i, err, idx;
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002485
2486 for (i = 0; i < AUTO_PIN_LAST; i++) {
2487 if (alc880_is_input_pin(cfg->input_pins[i])) {
Kailang Yangdf694da2005-12-05 19:42:22 +01002488 idx = alc880_input_pin_idx(cfg->input_pins[i]);
Takashi Iwai4a471b72005-12-07 13:56:29 +01002489 err = new_analog_input(spec, cfg->input_pins[i],
2490 auto_pin_cfg_labels[i],
Kailang Yangdf694da2005-12-05 19:42:22 +01002491 idx, 0x0b);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002492 if (err < 0)
2493 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002494 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002495 imux->items[imux->num_items].index = alc880_input_pin_idx(cfg->input_pins[i]);
2496 imux->num_items++;
2497 }
2498 }
2499 return 0;
2500}
2501
Kailang Yangdf694da2005-12-05 19:42:22 +01002502static void alc880_auto_set_output_and_unmute(struct hda_codec *codec,
2503 hda_nid_t nid, int pin_type,
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002504 int dac_idx)
2505{
2506 /* set as output */
2507 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2508 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2509 /* need the manual connection? */
2510 if (alc880_is_multi_pin(nid)) {
2511 struct alc_spec *spec = codec->spec;
2512 int idx = alc880_multi_pin_idx(nid);
2513 snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0,
2514 AC_VERB_SET_CONNECT_SEL,
2515 alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx]));
2516 }
2517}
2518
2519static void alc880_auto_init_multi_out(struct hda_codec *codec)
2520{
2521 struct alc_spec *spec = codec->spec;
2522 int i;
2523
2524 for (i = 0; i < spec->autocfg.line_outs; i++) {
2525 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2526 alc880_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2527 }
2528}
2529
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002530static void alc880_auto_init_extra_out(struct hda_codec *codec)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002531{
2532 struct alc_spec *spec = codec->spec;
2533 hda_nid_t pin;
2534
Takashi Iwai82bc9552006-03-21 11:24:42 +01002535 pin = spec->autocfg.speaker_pins[0];
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002536 if (pin) /* connect to front */
2537 alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002538 pin = spec->autocfg.hp_pin;
2539 if (pin) /* connect to front */
2540 alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2541}
2542
2543static void alc880_auto_init_analog_input(struct hda_codec *codec)
2544{
2545 struct alc_spec *spec = codec->spec;
2546 int i;
2547
2548 for (i = 0; i < AUTO_PIN_LAST; i++) {
2549 hda_nid_t nid = spec->autocfg.input_pins[i];
2550 if (alc880_is_input_pin(nid)) {
2551 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2552 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2553 if (nid != ALC880_PIN_CD_NID)
2554 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
2555 AMP_OUT_MUTE);
2556 }
2557 }
2558}
2559
2560/* parse the BIOS configuration and set up the alc_spec */
2561/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
2562static int alc880_parse_auto_config(struct hda_codec *codec)
2563{
2564 struct alc_spec *spec = codec->spec;
2565 int err;
Kailang Yangdf694da2005-12-05 19:42:22 +01002566 static hda_nid_t alc880_ignore[] = { 0x1d, 0 };
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002567
Kailang Yangdf694da2005-12-05 19:42:22 +01002568 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
2569 alc880_ignore)) < 0)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002570 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002571 if (! spec->autocfg.line_outs)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002572 return 0; /* can't find valid BIOS pin config */
Kailang Yangdf694da2005-12-05 19:42:22 +01002573
2574 if ((err = alc880_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 ||
2575 (err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01002576 (err = alc880_auto_create_extra_out(spec,
2577 spec->autocfg.speaker_pins[0],
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002578 "Speaker")) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01002579 (err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pin,
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002580 "Headphone")) < 0 ||
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002581 (err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
2582 return err;
2583
2584 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2585
2586 if (spec->autocfg.dig_out_pin)
2587 spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
2588 if (spec->autocfg.dig_in_pin)
2589 spec->dig_in_nid = ALC880_DIGIN_NID;
2590
2591 if (spec->kctl_alloc)
2592 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
2593
2594 spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs;
2595
2596 spec->input_mux = &spec->private_imux;
2597
2598 return 1;
2599}
2600
Takashi Iwaiae6b8132006-03-03 16:47:17 +01002601/* additional initialization for auto-configuration model */
2602static void alc880_auto_init(struct hda_codec *codec)
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002603{
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002604 alc880_auto_init_multi_out(codec);
Takashi Iwai8d88bc32005-11-17 11:09:23 +01002605 alc880_auto_init_extra_out(codec);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002606 alc880_auto_init_analog_input(codec);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002607}
2608
2609/*
2610 * OK, here we have finally the patch for ALC880
2611 */
2612
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613static int patch_alc880(struct hda_codec *codec)
2614{
2615 struct alc_spec *spec;
2616 int board_config;
Kailang Yangdf694da2005-12-05 19:42:22 +01002617 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002618
Takashi Iwaie560d8d2005-09-09 14:21:46 +02002619 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002620 if (spec == NULL)
2621 return -ENOMEM;
2622
2623 codec->spec = spec;
2624
2625 board_config = snd_hda_check_board_config(codec, alc880_cfg_tbl);
Takashi Iwai16ded522005-06-10 19:58:24 +02002626 if (board_config < 0 || board_config >= ALC880_MODEL_LAST) {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002627 printk(KERN_INFO "hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...\n");
2628 board_config = ALC880_AUTO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629 }
2630
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002631 if (board_config == ALC880_AUTO) {
2632 /* automatic parse from the BIOS config */
2633 err = alc880_parse_auto_config(codec);
2634 if (err < 0) {
2635 alc_free(codec);
2636 return err;
2637 } else if (! err) {
2638 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 3-stack mode...\n");
2639 board_config = ALC880_3ST;
2640 }
2641 }
2642
Kailang Yangdf694da2005-12-05 19:42:22 +01002643 if (board_config != ALC880_AUTO)
2644 setup_preset(spec, &alc880_presets[board_config]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002645
2646 spec->stream_name_analog = "ALC880 Analog";
2647 spec->stream_analog_playback = &alc880_pcm_analog_playback;
2648 spec->stream_analog_capture = &alc880_pcm_analog_capture;
2649
2650 spec->stream_name_digital = "ALC880 Digital";
2651 spec->stream_digital_playback = &alc880_pcm_digital_playback;
2652 spec->stream_digital_capture = &alc880_pcm_digital_capture;
2653
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002654 if (! spec->adc_nids && spec->input_mux) {
2655 /* check whether NID 0x07 is valid */
Takashi Iwai54d17402005-11-21 16:33:22 +01002656 unsigned int wcap = get_wcaps(codec, alc880_adc_nids[0]);
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002657 wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
2658 if (wcap != AC_WID_AUD_IN) {
2659 spec->adc_nids = alc880_adc_nids_alt;
2660 spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt);
2661 spec->mixers[spec->num_mixers] = alc880_capture_alt_mixer;
2662 spec->num_mixers++;
2663 } else {
2664 spec->adc_nids = alc880_adc_nids;
2665 spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
2666 spec->mixers[spec->num_mixers] = alc880_capture_mixer;
2667 spec->num_mixers++;
2668 }
2669 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670
2671 codec->patch_ops = alc_patch_ops;
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002672 if (board_config == ALC880_AUTO)
Takashi Iwaiae6b8132006-03-03 16:47:17 +01002673 spec->init_hook = alc880_auto_init;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002674
2675 return 0;
2676}
2677
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002678
Linus Torvalds1da177e2005-04-16 15:20:36 -07002679/*
2680 * ALC260 support
2681 */
2682
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002683static hda_nid_t alc260_dac_nids[1] = {
2684 /* front */
2685 0x02,
2686};
2687
2688static hda_nid_t alc260_adc_nids[1] = {
2689 /* ADC0 */
2690 0x04,
2691};
2692
Kailang Yangdf694da2005-12-05 19:42:22 +01002693static hda_nid_t alc260_adc_nids_alt[1] = {
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002694 /* ADC1 */
2695 0x05,
2696};
2697
Kailang Yangdf694da2005-12-05 19:42:22 +01002698static hda_nid_t alc260_hp_adc_nids[2] = {
2699 /* ADC1, 0 */
2700 0x05, 0x04
2701};
2702
Jonathan Woithed57fdac2006-02-28 11:38:35 +01002703/* NIDs used when simultaneous access to both ADCs makes sense. Note that
2704 * alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC.
2705 */
2706static hda_nid_t alc260_dual_adc_nids[2] = {
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01002707 /* ADC0, ADC1 */
2708 0x04, 0x05
2709};
2710
Takashi Iwaie9edcee2005-06-13 14:16:38 +02002711#define ALC260_DIGOUT_NID 0x03
2712#define ALC260_DIGIN_NID 0x06
2713
2714static struct hda_input_mux alc260_capture_source = {
2715 .num_items = 4,
2716 .items = {
2717 { "Mic", 0x0 },
2718 { "Front Mic", 0x1 },
2719 { "Line", 0x2 },
2720 { "CD", 0x4 },
2721 },
2722};
2723
Jonathan Woithe17e7aec2006-02-28 11:35:18 +01002724/* On Fujitsu S702x laptops capture only makes sense from Mic/LineIn jack,
2725 * headphone jack and the internal CD lines.
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002726 */
2727static struct hda_input_mux alc260_fujitsu_capture_source = {
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01002728 .num_items = 3,
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002729 .items = {
2730 { "Mic/Line", 0x0 },
2731 { "CD", 0x4 },
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01002732 { "Headphone", 0x2 },
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002733 },
2734};
2735
Jonathan Woithe0bfc90e2006-02-28 11:45:11 +01002736/* Acer TravelMate(/Extensa/Aspire) notebooks have similar configutation to
2737 * the Fujitsu S702x, but jacks are marked differently. We won't allow
2738 * retasking the Headphone jack, so it won't be available here.
2739 */
2740static struct hda_input_mux alc260_acer_capture_source = {
2741 .num_items = 3,
2742 .items = {
2743 { "Mic", 0x0 },
2744 { "Line", 0x2 },
2745 { "CD", 0x4 },
2746 },
2747};
2748
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749/*
2750 * This is just place-holder, so there's something for alc_build_pcms to look
2751 * at when it calculates the maximum number of channels. ALC260 has no mixer
2752 * element which allows changing the channel mode, so the verb list is
2753 * never used.
2754 */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +01002755static struct hda_channel_mode alc260_modes[1] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756 { 2, NULL },
2757};
2758
Kailang Yangdf694da2005-12-05 19:42:22 +01002759
2760/* Mixer combinations
2761 *
2762 * basic: base_output + input + pc_beep + capture
2763 * HP: base_output + input + capture_alt
2764 * HP_3013: hp_3013 + input + capture
2765 * fujitsu: fujitsu + capture
Jonathan Woithe0bfc90e2006-02-28 11:45:11 +01002766 * acer: acer + capture
Kailang Yangdf694da2005-12-05 19:42:22 +01002767 */
2768
2769static struct snd_kcontrol_new alc260_base_output_mixer[] = {
Takashi Iwai05acb862005-06-10 19:50:25 +02002770 HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01002771 HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
Kailang Yangdf694da2005-12-05 19:42:22 +01002772 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
2773 HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
2774 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2775 HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT),
2776 { } /* end */
2777};
2778
2779static struct snd_kcontrol_new alc260_input_mixer[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780 HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
2781 HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
2782 HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
2783 HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
2784 HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
2785 HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
2786 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT),
2787 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788 { } /* end */
2789};
2790
Kailang Yangdf694da2005-12-05 19:42:22 +01002791static struct snd_kcontrol_new alc260_pc_beep_mixer[] = {
2792 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x07, 0x05, HDA_INPUT),
2793 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x07, 0x05, HDA_INPUT),
2794 { } /* end */
2795};
2796
2797static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
2798 HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
2799 HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
2800 HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT),
2801 HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT),
2802 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
2803 HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
2804 HDA_CODEC_VOLUME_MONO("iSpeaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2805 HDA_CODEC_MUTE_MONO("iSpeaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
Takashi Iwai16ded522005-06-10 19:58:24 +02002806 { } /* end */
2807};
2808
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01002809static struct snd_kcontrol_new alc260_fujitsu_mixer[] = {
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002810 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01002811 HDA_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT),
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01002812 ALC_PIN_MODE("Headphone Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002813 HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
2814 HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
2815 HDA_CODEC_VOLUME("Mic/Line Playback Volume", 0x07, 0x0, HDA_INPUT),
2816 HDA_CODEC_MUTE("Mic/Line Playback Switch", 0x07, 0x0, HDA_INPUT),
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01002817 ALC_PIN_MODE("Mic/Line Jack Mode", 0x12, ALC_PIN_DIR_IN),
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002818 HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT),
2819 HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT),
2820 HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01002821 HDA_BIND_MUTE("Internal Speaker Playback Switch", 0x09, 2, HDA_INPUT),
Kailang Yangdf694da2005-12-05 19:42:22 +01002822 { } /* end */
2823};
2824
Jonathan Woithe0bfc90e2006-02-28 11:45:11 +01002825static struct snd_kcontrol_new alc260_acer_mixer[] = {
2826 HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
2827 HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT),
2828 HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
2829 HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
2830 HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
2831 HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
2832 ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN),
2833 HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
2834 HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
2835 ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
2836 HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT),
2837 HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT),
2838 { } /* end */
2839};
2840
Kailang Yangdf694da2005-12-05 19:42:22 +01002841/* capture mixer elements */
2842static struct snd_kcontrol_new alc260_capture_mixer[] = {
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002843 HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT),
2844 HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT),
Kailang Yangdf694da2005-12-05 19:42:22 +01002845 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x05, 0x0, HDA_INPUT),
2846 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x05, 0x0, HDA_INPUT),
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002847 {
2848 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Kailang Yangdf694da2005-12-05 19:42:22 +01002849 /* The multiple "Capture Source" controls confuse alsamixer
2850 * So call somewhat different..
2851 * FIXME: the controls appear in the "playback" view!
2852 */
2853 /* .name = "Capture Source", */
2854 .name = "Input Source",
2855 .count = 2,
Jonathan Woithea9430dd2005-09-16 19:12:48 +02002856 .info = alc_mux_enum_info,
2857 .get = alc_mux_enum_get,
2858 .put = alc_mux_enum_put,
2859 },
2860 { } /* end */
2861};
2862
Kailang Yangdf694da2005-12-05 19:42:22 +01002863static struct snd_kcontrol_new alc260_capture_alt_mixer[] = {
2864 HDA_CODEC_VOLUME("Capture Volume", 0x05, 0x0, HDA_INPUT),
2865 HDA_CODEC_MUTE("Capture Switch", 0x05, 0x0, HDA_INPUT),
2866 {
2867 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2868 /* The multiple "Capture Source" controls confuse alsamixer
2869 * So call somewhat different..
2870 * FIXME: the controls appear in the "playback" view!
2871 */
2872 /* .name = "Capture Source", */
2873 .name = "Input Source",
2874 .count = 1,
2875 .info = alc_mux_enum_info,
2876 .get = alc_mux_enum_get,
2877 .put = alc_mux_enum_put,
2878 },
2879 { } /* end */
2880};
2881
2882/*
2883 * initialization verbs
2884 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002885static struct hda_verb alc260_init_verbs[] = {
2886 /* Line In pin widget for input */
Takashi Iwai05acb862005-06-10 19:50:25 +02002887 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888 /* CD pin widget for input */
Takashi Iwai05acb862005-06-10 19:50:25 +02002889 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002890 /* Mic1 (rear panel) pin widget for input and vref at 80% */
Takashi Iwai16ded522005-06-10 19:58:24 +02002891 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002892 /* Mic2 (front panel) pin widget for input and vref at 80% */
Takashi Iwai16ded522005-06-10 19:58:24 +02002893 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002894 /* LINE-2 is used for line-out in rear */
Takashi Iwai05acb862005-06-10 19:50:25 +02002895 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002896 /* select line-out */
Jonathan Woithefd56f2d2006-01-24 10:35:46 +01002897 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002898 /* LINE-OUT pin */
Takashi Iwai05acb862005-06-10 19:50:25 +02002899 {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002900 /* enable HP */
Takashi Iwai05acb862005-06-10 19:50:25 +02002901 {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002902 /* enable Mono */
Takashi Iwai05acb862005-06-10 19:50:25 +02002903 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2904 /* mute capture amp left and right */
Takashi Iwai16ded522005-06-10 19:58:24 +02002905 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002906 /* set connection select to line in (default select for this ADC) */
2907 {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
Takashi Iwai16ded522005-06-10 19:58:24 +02002908 /* mute capture amp left and right */
2909 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2910 /* set connection select to line in (default select for this ADC) */
2911 {0x05, AC_VERB_SET_CONNECT_SEL, 0x02},
Takashi Iwai05acb862005-06-10 19:50:25 +02002912 /* set vol=0 Line-Out mixer amp left and right */
2913 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2914 /* unmute pin widget amp left and right (no gain on this amp) */
2915 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2916 /* set vol=0 HP mixer amp left and right */
2917 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2918 /* unmute pin widget amp left and right (no gain on this amp) */
2919 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2920 /* set vol=0 Mono mixer amp left and right */
2921 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2922 /* unmute pin widget amp left and right (no gain on this amp) */
2923 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2924 /* unmute LINE-2 out pin */
2925 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002926 /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
Takashi Iwai05acb862005-06-10 19:50:25 +02002927 /* mute CD */
Takashi Iwai16ded522005-06-10 19:58:24 +02002928 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
Takashi Iwai05acb862005-06-10 19:50:25 +02002929 /* mute Line In */
Takashi Iwai16ded522005-06-10 19:58:24 +02002930 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
Takashi Iwai05acb862005-06-10 19:50:25 +02002931 /* mute Mic */
Takashi Iwai16ded522005-06-10 19:58:24 +02002932 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002933 /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
Takashi Iwai05acb862005-06-10 19:50:25 +02002934 /* mute Front out path */
2935 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2936 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2937 /* mute Headphone out path */
2938 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2939 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2940 /* mute Mono out path */
2941 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2942 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002943 { }
2944};
2945
Kailang Yangdf694da2005-12-05 19:42:22 +01002946static struct hda_verb alc260_hp_init_verbs[] = {
2947 /* Headphone and output */
2948 {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
2949 /* mono output */
2950 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2951 /* Mic1 (rear panel) pin widget for input and vref at 80% */
2952 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
2953 /* Mic2 (front panel) pin widget for input and vref at 80% */
2954 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
2955 /* Line In pin widget for input */
2956 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2957 /* Line-2 pin widget for output */
2958 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2959 /* CD pin widget for input */
2960 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2961 /* unmute amp left and right */
2962 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
2963 /* set connection select to line in (default select for this ADC) */
2964 {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
2965 /* unmute Line-Out mixer amp left and right (volume = 0) */
2966 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
2967 /* mute pin widget amp left and right (no gain on this amp) */
2968 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
2969 /* unmute HP mixer amp left and right (volume = 0) */
2970 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
2971 /* mute pin widget amp left and right (no gain on this amp) */
2972 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
2973 /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
2974 /* unmute CD */
2975 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
2976 /* unmute Line In */
2977 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
2978 /* unmute Mic */
2979 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
2980 /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
2981 /* Unmute Front out path */
2982 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
2983 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
2984 /* Unmute Headphone out path */
2985 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
2986 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
2987 /* Unmute Mono out path */
2988 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
2989 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
2990 { }
2991};
2992
2993static struct hda_verb alc260_hp_3013_init_verbs[] = {
2994 /* Line out and output */
2995 {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2996 /* mono output */
2997 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2998 /* Mic1 (rear panel) pin widget for input and vref at 80% */
2999 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
3000 /* Mic2 (front panel) pin widget for input and vref at 80% */
3001 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
3002 /* Line In pin widget for input */
3003 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
3004 /* Headphone pin widget for output */
3005 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
3006 /* CD pin widget for input */
3007 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
3008 /* unmute amp left and right */
3009 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
3010 /* set connection select to line in (default select for this ADC) */
3011 {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
3012 /* unmute Line-Out mixer amp left and right (volume = 0) */
3013 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
3014 /* mute pin widget amp left and right (no gain on this amp) */
3015 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
3016 /* unmute HP mixer amp left and right (volume = 0) */
3017 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
3018 /* mute pin widget amp left and right (no gain on this amp) */
3019 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
3020 /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
3021 /* unmute CD */
3022 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
3023 /* unmute Line In */
3024 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
3025 /* unmute Mic */
3026 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
3027 /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
3028 /* Unmute Front out path */
3029 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
3030 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
3031 /* Unmute Headphone out path */
3032 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
3033 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
3034 /* Unmute Mono out path */
3035 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
3036 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
3037 { }
3038};
3039
Jonathan Woithea9430dd2005-09-16 19:12:48 +02003040/* Initialisation sequence for ALC260 as configured in Fujitsu S702x
3041 * laptops.
3042 */
3043static struct hda_verb alc260_fujitsu_init_verbs[] = {
3044 /* Disable all GPIOs */
3045 {0x01, AC_VERB_SET_GPIO_MASK, 0},
3046 /* Internal speaker is connected to headphone pin */
3047 {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3048 /* Headphone/Line-out jack connects to Line1 pin; make it an output */
3049 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Jonathan Woithef7ace402006-02-28 11:46:14 +01003050 /* Mic/Line-in jack is connected to mic1 pin, so make it an input */
3051 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3052 /* Ensure all other unused pins are disabled and muted. */
3053 {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
3054 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Jonathan Woithea9430dd2005-09-16 19:12:48 +02003055 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
Jonathan Woithef7ace402006-02-28 11:46:14 +01003056 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Jonathan Woithea9430dd2005-09-16 19:12:48 +02003057 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
Jonathan Woithef7ace402006-02-28 11:46:14 +01003058 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3059 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
3060 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Jonathan Woithea9430dd2005-09-16 19:12:48 +02003061
Jonathan Woithef7ace402006-02-28 11:46:14 +01003062 /* Disable digital (SPDIF) pins */
3063 {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
3064 {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01003065
Jonathan Woithef7ace402006-02-28 11:46:14 +01003066 /* Ensure Line1 pin widget takes its input from the OUT1 sum bus
3067 * when acting as an output.
3068 */
3069 {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
3070
3071 /* Start with output sum widgets muted and their output gains at min */
Takashi Iwai8b33a5a2006-02-09 11:57:01 +01003072 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3073 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3074 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3075 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3076 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3077 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3078 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3079 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3080 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Jonathan Woithea9430dd2005-09-16 19:12:48 +02003081
Jonathan Woithef7ace402006-02-28 11:46:14 +01003082 /* Unmute HP pin widget amp left and right (no equiv mixer ctrl) */
3083 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3084 /* Unmute Line1 pin widget output buffer since it starts as an output.
3085 * If the pin mode is changed by the user the pin mode control will
3086 * take care of enabling the pin's input/output buffers as needed.
3087 * Therefore there's no need to enable the input buffer at this
3088 * stage.
Jonathan Woithecdcd9262006-02-28 11:36:42 +01003089 */
Jonathan Woithef7ace402006-02-28 11:46:14 +01003090 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Jonathan Woithecdcd9262006-02-28 11:36:42 +01003091 /* Unmute input buffer of pin widget used for Line-in (no equiv
3092 * mixer ctrl)
3093 */
Jonathan Woithef7ace402006-02-28 11:46:14 +01003094 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Jonathan Woithea9430dd2005-09-16 19:12:48 +02003095
Jonathan Woithef7ace402006-02-28 11:46:14 +01003096 /* Mute capture amp left and right */
3097 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3098 /* Set ADC connection select to match default mixer setting - line
3099 * in (on mic1 pin)
3100 */
3101 {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
Jonathan Woithea9430dd2005-09-16 19:12:48 +02003102
Jonathan Woithef7ace402006-02-28 11:46:14 +01003103 /* Do the same for the second ADC: mute capture input amp and
3104 * set ADC connection to line in (on mic1 pin)
3105 */
3106 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3107 {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
Jonathan Woithe4c5186e2006-02-09 11:53:48 +01003108
Jonathan Woithef7ace402006-02-28 11:46:14 +01003109 /* Mute all inputs to mixer widget (even unconnected ones) */
3110 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
3111 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
3112 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
3113 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
3114 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
3115 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
3116 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
3117 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
Takashi Iwai4a471b72005-12-07 13:56:29 +01003118
3119 { }
Jonathan Woithea9430dd2005-09-16 19:12:48 +02003120};
3121
Jonathan Woithe0bfc90e2006-02-28 11:45:11 +01003122/* Initialisation sequence for ALC260 as configured in Acer TravelMate and
3123 * similar laptops (adapted from Fujitsu init verbs).
3124 */
3125static struct hda_verb alc260_acer_init_verbs[] = {
3126 /* On TravelMate laptops, GPIO 0 enables the internal speaker and
3127 * the headphone jack. Turn this on and rely on the standard mute
3128 * methods whenever the user wants to turn these outputs off.
3129 */
3130 {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
3131 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
3132 {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
3133 /* Internal speaker/Headphone jack is connected to Line-out pin */
3134 {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3135 /* Internal microphone/Mic jack is connected to Mic1 pin */
3136 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
3137 /* Line In jack is connected to Line1 pin */
3138 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3139 /* Ensure all other unused pins are disabled and muted. */
3140 {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
3141 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3142 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
3143 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3144 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
3145 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3146 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
3147 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3148 /* Disable digital (SPDIF) pins */
3149 {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
3150 {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
3151
3152 /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum
3153 * bus when acting as outputs.
3154 */
3155 {0x0b, AC_VERB_SET_CONNECT_SEL, 0},
3156 {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
3157
3158 /* Start with output sum widgets muted and their output gains at min */
3159 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3160 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3161 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3162 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3163 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3164 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3165 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3166 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3167 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3168
3169 /* Unmute Line-out pin widget amp left and right (no equiv mixer ctrl) */
3170 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3171 /* Unmute Mic1 and Line1 pin widget input buffers since they start as
3172 * inputs. If the pin mode is changed by the user the pin mode control
3173 * will take care of enabling the pin's input/output buffers as needed.
3174 * Therefore there's no need to enable the input buffer at this
3175 * stage.
3176 */
3177 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3178 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3179
3180 /* Mute capture amp left and right */
3181 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3182 /* Set ADC connection select to match default mixer setting - mic
3183 * (on mic1 pin)
3184 */
3185 {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
3186
3187 /* Do similar with the second ADC: mute capture input amp and
3188 * set ADC connection to line (on line1 pin)
3189 */
3190 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3191 {0x05, AC_VERB_SET_CONNECT_SEL, 0x02},
3192
3193 /* Mute all inputs to mixer widget (even unconnected ones) */
3194 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
3195 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
3196 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
3197 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
3198 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
3199 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
3200 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
3201 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
3202
3203 { }
3204};
3205
Jonathan Woithe7cf51e42006-02-09 12:01:26 +01003206/* Test configuration for debugging, modelled after the ALC880 test
3207 * configuration.
3208 */
3209#ifdef CONFIG_SND_DEBUG
3210static hda_nid_t alc260_test_dac_nids[1] = {
3211 0x02,
3212};
3213static hda_nid_t alc260_test_adc_nids[2] = {
3214 0x04, 0x05,
3215};
Jonathan Woithe17e7aec2006-02-28 11:35:18 +01003216/* This is a bit messy since the two input muxes in the ALC260 have slight
3217 * variations in their signal assignments. The ideal way to deal with this
3218 * is to extend alc_spec.input_mux to allow a different input MUX for each
3219 * ADC. For the purposes of the test model it's sufficient to just list
3220 * both options for affected signal indices. The separate input mux
3221 * functionality only needs to be considered if a model comes along which
3222 * actually uses signals 0x5, 0x6 and 0x7 for something which makes sense to
3223 * record.
3224 */
Jonathan Woithe7cf51e42006-02-09 12:01:26 +01003225static struct hda_input_mux alc260_test_capture_source = {
Jonathan Woithe17e7aec2006-02-28 11:35:18 +01003226 .num_items = 8,
Jonathan Woithe7cf51e42006-02-09 12:01:26 +01003227 .items = {
3228 { "MIC1 pin", 0x0 },
3229 { "MIC2 pin", 0x1 },
3230 { "LINE1 pin", 0x2 },
3231 { "LINE2 pin", 0x3 },
3232 { "CD pin", 0x4 },
Jonathan Woithe17e7aec2006-02-28 11:35:18 +01003233 { "LINE-OUT pin (cap1), Mixer (cap2)", 0x5 },
3234 { "HP-OUT pin (cap1), LINE-OUT pin (cap2)", 0x6 },
3235 { "HP-OUT pin (cap2 only)", 0x7 },
Jonathan Woithe7cf51e42006-02-09 12:01:26 +01003236 },
3237};
3238static struct snd_kcontrol_new alc260_test_mixer[] = {
3239 /* Output driver widgets */
3240 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
3241 HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT),
3242 HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x09, 0x0, HDA_OUTPUT),
3243 HDA_BIND_MUTE("LOUT2 Playback Switch", 0x09, 2, HDA_INPUT),
3244 HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x08, 0x0, HDA_OUTPUT),
3245 HDA_BIND_MUTE("LOUT1 Playback Switch", 0x08, 2, HDA_INPUT),
3246
3247 /* Modes for retasking pin widgets */
3248 ALC_PIN_MODE("HP-OUT pin mode", 0x10, ALC_PIN_DIR_INOUT),
3249 ALC_PIN_MODE("LINE-OUT pin mode", 0x0f, ALC_PIN_DIR_INOUT),
3250 ALC_PIN_MODE("LINE2 pin mode", 0x15, ALC_PIN_DIR_INOUT),
3251 ALC_PIN_MODE("LINE1 pin mode", 0x14, ALC_PIN_DIR_INOUT),
3252 ALC_PIN_MODE("MIC2 pin mode", 0x13, ALC_PIN_DIR_INOUT),
3253 ALC_PIN_MODE("MIC1 pin mode", 0x12, ALC_PIN_DIR_INOUT),
3254
3255 /* Loopback mixer controls */
3256 HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x07, 0x00, HDA_INPUT),
3257 HDA_CODEC_MUTE("MIC1 Playback Switch", 0x07, 0x00, HDA_INPUT),
3258 HDA_CODEC_VOLUME("MIC2 Playback Volume", 0x07, 0x01, HDA_INPUT),
3259 HDA_CODEC_MUTE("MIC2 Playback Switch", 0x07, 0x01, HDA_INPUT),
3260 HDA_CODEC_VOLUME("LINE1 Playback Volume", 0x07, 0x02, HDA_INPUT),
3261 HDA_CODEC_MUTE("LINE1 Playback Switch", 0x07, 0x02, HDA_INPUT),
3262 HDA_CODEC_VOLUME("LINE2 Playback Volume", 0x07, 0x03, HDA_INPUT),
3263 HDA_CODEC_MUTE("LINE2 Playback Switch", 0x07, 0x03, HDA_INPUT),
3264 HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
3265 HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
3266 HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT),
3267 HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT),
3268 HDA_CODEC_VOLUME("LINE-OUT loopback Playback Volume", 0x07, 0x06, HDA_INPUT),
3269 HDA_CODEC_MUTE("LINE-OUT loopback Playback Switch", 0x07, 0x06, HDA_INPUT),
3270 HDA_CODEC_VOLUME("HP-OUT loopback Playback Volume", 0x07, 0x7, HDA_INPUT),
3271 HDA_CODEC_MUTE("HP-OUT loopback Playback Switch", 0x07, 0x7, HDA_INPUT),
Jonathan Woithe5c8f8582006-02-28 11:43:27 +01003272
3273 /* Controls for GPIO pins, assuming they are configured as outputs */
3274 ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01),
3275 ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02),
3276 ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04),
3277 ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08),
3278
Jonathan Woithe92621f12006-02-28 11:47:47 +01003279 /* Switches to allow the digital IO pins to be enabled. The datasheet
3280 * is ambigious as to which NID is which; testing on laptops which
3281 * make this output available should provide clarification.
3282 */
3283 ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01),
3284 ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01),
3285
Jonathan Woithe7cf51e42006-02-09 12:01:26 +01003286 { } /* end */
3287};
3288static struct hda_verb alc260_test_init_verbs[] = {
Jonathan Woithe5c8f8582006-02-28 11:43:27 +01003289 /* Enable all GPIOs as outputs with an initial value of 0 */
3290 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x0f},
3291 {0x01, AC_VERB_SET_GPIO_DATA, 0x00},
3292 {0x01, AC_VERB_SET_GPIO_MASK, 0x0f},
3293
Jonathan Woithe7cf51e42006-02-09 12:01:26 +01003294 /* Enable retasking pins as output, initially without power amp */
3295 {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3296 {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3297 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3298 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3299 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3300 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3301
Jonathan Woithe92621f12006-02-28 11:47:47 +01003302 /* Disable digital (SPDIF) pins initially, but users can enable
3303 * them via a mixer switch. In the case of SPDIF-out, this initverb
3304 * payload also sets the generation to 0, output to be in "consumer"
3305 * PCM format, copyright asserted, no pre-emphasis and no validity
3306 * control.
3307 */
Jonathan Woithe7cf51e42006-02-09 12:01:26 +01003308 {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
3309 {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
3310
Jonathan Woithef7ace402006-02-28 11:46:14 +01003311 /* Ensure mic1, mic2, line1 and line2 pin widgets take input from the
Jonathan Woithe7cf51e42006-02-09 12:01:26 +01003312 * OUT1 sum bus when acting as an output.
3313 */
3314 {0x0b, AC_VERB_SET_CONNECT_SEL, 0},
3315 {0x0c, AC_VERB_SET_CONNECT_SEL, 0},
3316 {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
3317 {0x0e, AC_VERB_SET_CONNECT_SEL, 0},
3318
3319 /* Start with output sum widgets muted and their output gains at min */
3320 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3321 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3322 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3323 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3324 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3325 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3326 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3327 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3328 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3329
Jonathan Woithecdcd9262006-02-28 11:36:42 +01003330 /* Unmute retasking pin widget output buffers since the default
3331 * state appears to be output. As the pin mode is changed by the
3332 * user the pin mode control will take care of enabling the pin's
3333 * input/output buffers as needed.
3334 */
Jonathan Woithe7cf51e42006-02-09 12:01:26 +01003335 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3336 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3337 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3338 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3339 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3340 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3341 /* Also unmute the mono-out pin widget */
3342 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3343
Jonathan Woithe7cf51e42006-02-09 12:01:26 +01003344 /* Mute capture amp left and right */
3345 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Jonathan Woithef7ace402006-02-28 11:46:14 +01003346 /* Set ADC connection select to match default mixer setting (mic1
3347 * pin)
Jonathan Woithe7cf51e42006-02-09 12:01:26 +01003348 */
3349 {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
3350
3351 /* Do the same for the second ADC: mute capture input amp and
Jonathan Woithef7ace402006-02-28 11:46:14 +01003352 * set ADC connection to mic1 pin
Jonathan Woithe7cf51e42006-02-09 12:01:26 +01003353 */
3354 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3355 {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
3356
3357 /* Mute all inputs to mixer widget (even unconnected ones) */
3358 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
3359 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
3360 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
3361 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
3362 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
3363 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
3364 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
3365 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
3366
3367 { }
3368};
3369#endif
3370
Linus Torvalds1da177e2005-04-16 15:20:36 -07003371static struct hda_pcm_stream alc260_pcm_analog_playback = {
3372 .substreams = 1,
3373 .channels_min = 2,
3374 .channels_max = 2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003375};
3376
3377static struct hda_pcm_stream alc260_pcm_analog_capture = {
3378 .substreams = 1,
3379 .channels_min = 2,
3380 .channels_max = 2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003381};
3382
Takashi Iwaia3bcba32005-12-06 19:05:29 +01003383#define alc260_pcm_digital_playback alc880_pcm_digital_playback
3384#define alc260_pcm_digital_capture alc880_pcm_digital_capture
3385
Kailang Yangdf694da2005-12-05 19:42:22 +01003386/*
3387 * for BIOS auto-configuration
3388 */
3389
3390static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
3391 const char *pfx)
3392{
3393 hda_nid_t nid_vol;
3394 unsigned long vol_val, sw_val;
3395 char name[32];
3396 int err;
3397
3398 if (nid >= 0x0f && nid < 0x11) {
3399 nid_vol = nid - 0x7;
3400 vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
3401 sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
3402 } else if (nid == 0x11) {
3403 nid_vol = nid - 0x7;
3404 vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT);
3405 sw_val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
3406 } else if (nid >= 0x12 && nid <= 0x15) {
3407 nid_vol = 0x08;
3408 vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
3409 sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
3410 } else
3411 return 0; /* N/A */
3412
3413 snprintf(name, sizeof(name), "%s Playback Volume", pfx);
3414 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val)) < 0)
3415 return err;
3416 snprintf(name, sizeof(name), "%s Playback Switch", pfx);
3417 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, sw_val)) < 0)
3418 return err;
3419 return 1;
3420}
3421
3422/* add playback controls from the parsed DAC table */
3423static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
3424 const struct auto_pin_cfg *cfg)
3425{
3426 hda_nid_t nid;
3427 int err;
3428
3429 spec->multiout.num_dacs = 1;
3430 spec->multiout.dac_nids = spec->private_dac_nids;
3431 spec->multiout.dac_nids[0] = 0x02;
3432
3433 nid = cfg->line_out_pins[0];
3434 if (nid) {
3435 err = alc260_add_playback_controls(spec, nid, "Front");
3436 if (err < 0)
3437 return err;
3438 }
3439
Takashi Iwai82bc9552006-03-21 11:24:42 +01003440 nid = cfg->speaker_pins[0];
Kailang Yangdf694da2005-12-05 19:42:22 +01003441 if (nid) {
3442 err = alc260_add_playback_controls(spec, nid, "Speaker");
3443 if (err < 0)
3444 return err;
3445 }
3446
3447 nid = cfg->hp_pin;
3448 if (nid) {
3449 err = alc260_add_playback_controls(spec, nid, "Headphone");
3450 if (err < 0)
3451 return err;
3452 }
3453 return 0;
3454}
3455
3456/* create playback/capture controls for input pins */
3457static int alc260_auto_create_analog_input_ctls(struct alc_spec *spec,
3458 const struct auto_pin_cfg *cfg)
3459{
Kailang Yangdf694da2005-12-05 19:42:22 +01003460 struct hda_input_mux *imux = &spec->private_imux;
3461 int i, err, idx;
3462
3463 for (i = 0; i < AUTO_PIN_LAST; i++) {
3464 if (cfg->input_pins[i] >= 0x12) {
3465 idx = cfg->input_pins[i] - 0x12;
Takashi Iwai4a471b72005-12-07 13:56:29 +01003466 err = new_analog_input(spec, cfg->input_pins[i],
3467 auto_pin_cfg_labels[i], idx, 0x07);
Kailang Yangdf694da2005-12-05 19:42:22 +01003468 if (err < 0)
3469 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01003470 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Kailang Yangdf694da2005-12-05 19:42:22 +01003471 imux->items[imux->num_items].index = idx;
3472 imux->num_items++;
3473 }
3474 if ((cfg->input_pins[i] >= 0x0f) && (cfg->input_pins[i] <= 0x10)){
3475 idx = cfg->input_pins[i] - 0x09;
Takashi Iwai4a471b72005-12-07 13:56:29 +01003476 err = new_analog_input(spec, cfg->input_pins[i],
3477 auto_pin_cfg_labels[i], idx, 0x07);
Kailang Yangdf694da2005-12-05 19:42:22 +01003478 if (err < 0)
3479 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01003480 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Kailang Yangdf694da2005-12-05 19:42:22 +01003481 imux->items[imux->num_items].index = idx;
3482 imux->num_items++;
3483 }
3484 }
3485 return 0;
3486}
3487
3488static void alc260_auto_set_output_and_unmute(struct hda_codec *codec,
3489 hda_nid_t nid, int pin_type,
3490 int sel_idx)
3491{
3492 /* set as output */
3493 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
3494 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
3495 /* need the manual connection? */
3496 if (nid >= 0x12) {
3497 int idx = nid - 0x12;
3498 snd_hda_codec_write(codec, idx + 0x0b, 0,
3499 AC_VERB_SET_CONNECT_SEL, sel_idx);
3500
3501 }
3502}
3503
3504static void alc260_auto_init_multi_out(struct hda_codec *codec)
3505{
3506 struct alc_spec *spec = codec->spec;
3507 hda_nid_t nid;
3508
3509 nid = spec->autocfg.line_out_pins[0];
3510 if (nid)
3511 alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
3512
Takashi Iwai82bc9552006-03-21 11:24:42 +01003513 nid = spec->autocfg.speaker_pins[0];
Kailang Yangdf694da2005-12-05 19:42:22 +01003514 if (nid)
3515 alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
3516
3517 nid = spec->autocfg.hp_pin;
3518 if (nid)
3519 alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
3520}
3521
3522#define ALC260_PIN_CD_NID 0x16
3523static void alc260_auto_init_analog_input(struct hda_codec *codec)
3524{
3525 struct alc_spec *spec = codec->spec;
3526 int i;
3527
3528 for (i = 0; i < AUTO_PIN_LAST; i++) {
3529 hda_nid_t nid = spec->autocfg.input_pins[i];
3530 if (nid >= 0x12) {
3531 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
3532 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
3533 if (nid != ALC260_PIN_CD_NID)
3534 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
3535 AMP_OUT_MUTE);
3536 }
3537 }
3538}
3539
3540/*
3541 * generic initialization of ADC, input mixers and output mixers
3542 */
3543static struct hda_verb alc260_volume_init_verbs[] = {
3544 /*
3545 * Unmute ADC0-1 and set the default input to mic-in
3546 */
3547 {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
3548 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3549 {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
3550 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3551
3552 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3553 * mixer widget
3554 * Note: PASD motherboards uses the Line In 2 as the input for front panel
3555 * mic (mic 2)
3556 */
3557 /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
3558 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3559 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3560 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3561 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3562 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3563
3564 /*
3565 * Set up output mixers (0x08 - 0x0a)
3566 */
3567 /* set vol=0 to output mixers */
3568 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3569 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3570 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3571 /* set up input amps for analog loopback */
3572 /* Amp Indices: DAC = 0, mixer = 1 */
3573 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3574 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3575 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3576 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3577 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3578 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3579
3580 { }
3581};
3582
3583static int alc260_parse_auto_config(struct hda_codec *codec)
3584{
3585 struct alc_spec *spec = codec->spec;
3586 unsigned int wcap;
3587 int err;
3588 static hda_nid_t alc260_ignore[] = { 0x17, 0 };
3589
3590 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
3591 alc260_ignore)) < 0)
3592 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01003593 if ((err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0)
3594 return err;
3595 if (! spec->kctl_alloc)
Kailang Yangdf694da2005-12-05 19:42:22 +01003596 return 0; /* can't find valid BIOS pin config */
Takashi Iwai4a471b72005-12-07 13:56:29 +01003597 if ((err = alc260_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
Kailang Yangdf694da2005-12-05 19:42:22 +01003598 return err;
3599
3600 spec->multiout.max_channels = 2;
3601
3602 if (spec->autocfg.dig_out_pin)
3603 spec->multiout.dig_out_nid = ALC260_DIGOUT_NID;
3604 if (spec->kctl_alloc)
3605 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
3606
3607 spec->init_verbs[spec->num_init_verbs++] = alc260_volume_init_verbs;
3608
3609 spec->input_mux = &spec->private_imux;
3610
3611 /* check whether NID 0x04 is valid */
Takashi Iwai4a471b72005-12-07 13:56:29 +01003612 wcap = get_wcaps(codec, 0x04);
Kailang Yangdf694da2005-12-05 19:42:22 +01003613 wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
3614 if (wcap != AC_WID_AUD_IN) {
3615 spec->adc_nids = alc260_adc_nids_alt;
3616 spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
3617 spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer;
Kailang Yangdf694da2005-12-05 19:42:22 +01003618 } else {
3619 spec->adc_nids = alc260_adc_nids;
3620 spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
3621 spec->mixers[spec->num_mixers] = alc260_capture_mixer;
Kailang Yangdf694da2005-12-05 19:42:22 +01003622 }
Takashi Iwai4a471b72005-12-07 13:56:29 +01003623 spec->num_mixers++;
Kailang Yangdf694da2005-12-05 19:42:22 +01003624
3625 return 1;
3626}
3627
Takashi Iwaiae6b8132006-03-03 16:47:17 +01003628/* additional initialization for auto-configuration model */
3629static void alc260_auto_init(struct hda_codec *codec)
Kailang Yangdf694da2005-12-05 19:42:22 +01003630{
Kailang Yangdf694da2005-12-05 19:42:22 +01003631 alc260_auto_init_multi_out(codec);
3632 alc260_auto_init_analog_input(codec);
Kailang Yangdf694da2005-12-05 19:42:22 +01003633}
3634
3635/*
3636 * ALC260 configurations
3637 */
Takashi Iwai16ded522005-06-10 19:58:24 +02003638static struct hda_board_config alc260_cfg_tbl[] = {
Kailang Yangdf694da2005-12-05 19:42:22 +01003639 { .modelname = "basic", .config = ALC260_BASIC },
Takashi Iwaib14e77e2006-01-11 18:10:50 +01003640 { .pci_subvendor = 0x104d, .pci_subdevice = 0x81bb,
3641 .config = ALC260_BASIC }, /* Sony VAIO */
Takashi Iwaic1fc8042006-03-06 14:17:31 +01003642 { .pci_subvendor = 0x152d, .pci_subdevice = 0x0729,
3643 .config = ALC260_BASIC }, /* CTL Travel Master U553W */
Takashi Iwai16ded522005-06-10 19:58:24 +02003644 { .modelname = "hp", .config = ALC260_HP },
Kailang Yangdf694da2005-12-05 19:42:22 +01003645 { .pci_subvendor = 0x103c, .pci_subdevice = 0x3010, .config = ALC260_HP },
3646 { .pci_subvendor = 0x103c, .pci_subdevice = 0x3011, .config = ALC260_HP },
3647 { .pci_subvendor = 0x103c, .pci_subdevice = 0x3012, .config = ALC260_HP },
3648 { .pci_subvendor = 0x103c, .pci_subdevice = 0x3013, .config = ALC260_HP_3013 },
3649 { .pci_subvendor = 0x103c, .pci_subdevice = 0x3014, .config = ALC260_HP },
3650 { .pci_subvendor = 0x103c, .pci_subdevice = 0x3015, .config = ALC260_HP },
3651 { .pci_subvendor = 0x103c, .pci_subdevice = 0x3016, .config = ALC260_HP },
3652 { .modelname = "fujitsu", .config = ALC260_FUJITSU_S702X },
3653 { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1326, .config = ALC260_FUJITSU_S702X },
Jonathan Woithe0bfc90e2006-02-28 11:45:11 +01003654 { .modelname = "acer", .config = ALC260_ACER },
3655 { .pci_subvendor = 0x1025, .pci_subdevice = 0x008f, .config = ALC260_ACER },
Jonathan Woithe7cf51e42006-02-09 12:01:26 +01003656#ifdef CONFIG_SND_DEBUG
3657 { .modelname = "test", .config = ALC260_TEST },
3658#endif
Kailang Yangdf694da2005-12-05 19:42:22 +01003659 { .modelname = "auto", .config = ALC260_AUTO },
Takashi Iwai16ded522005-06-10 19:58:24 +02003660 {}
3661};
3662
Kailang Yangdf694da2005-12-05 19:42:22 +01003663static struct alc_config_preset alc260_presets[] = {
3664 [ALC260_BASIC] = {
3665 .mixers = { alc260_base_output_mixer,
3666 alc260_input_mixer,
3667 alc260_pc_beep_mixer,
3668 alc260_capture_mixer },
3669 .init_verbs = { alc260_init_verbs },
3670 .num_dacs = ARRAY_SIZE(alc260_dac_nids),
3671 .dac_nids = alc260_dac_nids,
3672 .num_adc_nids = ARRAY_SIZE(alc260_adc_nids),
3673 .adc_nids = alc260_adc_nids,
3674 .num_channel_mode = ARRAY_SIZE(alc260_modes),
3675 .channel_mode = alc260_modes,
3676 .input_mux = &alc260_capture_source,
3677 },
3678 [ALC260_HP] = {
3679 .mixers = { alc260_base_output_mixer,
3680 alc260_input_mixer,
3681 alc260_capture_alt_mixer },
3682 .init_verbs = { alc260_hp_init_verbs },
3683 .num_dacs = ARRAY_SIZE(alc260_dac_nids),
3684 .dac_nids = alc260_dac_nids,
3685 .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
3686 .adc_nids = alc260_hp_adc_nids,
3687 .num_channel_mode = ARRAY_SIZE(alc260_modes),
3688 .channel_mode = alc260_modes,
3689 .input_mux = &alc260_capture_source,
3690 },
3691 [ALC260_HP_3013] = {
3692 .mixers = { alc260_hp_3013_mixer,
3693 alc260_input_mixer,
3694 alc260_capture_alt_mixer },
3695 .init_verbs = { alc260_hp_3013_init_verbs },
3696 .num_dacs = ARRAY_SIZE(alc260_dac_nids),
3697 .dac_nids = alc260_dac_nids,
3698 .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
3699 .adc_nids = alc260_hp_adc_nids,
3700 .num_channel_mode = ARRAY_SIZE(alc260_modes),
3701 .channel_mode = alc260_modes,
3702 .input_mux = &alc260_capture_source,
3703 },
3704 [ALC260_FUJITSU_S702X] = {
3705 .mixers = { alc260_fujitsu_mixer,
3706 alc260_capture_mixer },
3707 .init_verbs = { alc260_fujitsu_init_verbs },
3708 .num_dacs = ARRAY_SIZE(alc260_dac_nids),
3709 .dac_nids = alc260_dac_nids,
Jonathan Woithed57fdac2006-02-28 11:38:35 +01003710 .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
3711 .adc_nids = alc260_dual_adc_nids,
Kailang Yangdf694da2005-12-05 19:42:22 +01003712 .num_channel_mode = ARRAY_SIZE(alc260_modes),
3713 .channel_mode = alc260_modes,
3714 .input_mux = &alc260_fujitsu_capture_source,
3715 },
Jonathan Woithe0bfc90e2006-02-28 11:45:11 +01003716 [ALC260_ACER] = {
3717 .mixers = { alc260_acer_mixer,
3718 alc260_capture_mixer },
3719 .init_verbs = { alc260_acer_init_verbs },
3720 .num_dacs = ARRAY_SIZE(alc260_dac_nids),
3721 .dac_nids = alc260_dac_nids,
3722 .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
3723 .adc_nids = alc260_dual_adc_nids,
3724 .num_channel_mode = ARRAY_SIZE(alc260_modes),
3725 .channel_mode = alc260_modes,
3726 .input_mux = &alc260_acer_capture_source,
3727 },
Jonathan Woithe7cf51e42006-02-09 12:01:26 +01003728#ifdef CONFIG_SND_DEBUG
3729 [ALC260_TEST] = {
3730 .mixers = { alc260_test_mixer,
3731 alc260_capture_mixer },
3732 .init_verbs = { alc260_test_init_verbs },
3733 .num_dacs = ARRAY_SIZE(alc260_test_dac_nids),
3734 .dac_nids = alc260_test_dac_nids,
3735 .num_adc_nids = ARRAY_SIZE(alc260_test_adc_nids),
3736 .adc_nids = alc260_test_adc_nids,
3737 .num_channel_mode = ARRAY_SIZE(alc260_modes),
3738 .channel_mode = alc260_modes,
3739 .input_mux = &alc260_test_capture_source,
3740 },
3741#endif
Kailang Yangdf694da2005-12-05 19:42:22 +01003742};
3743
Linus Torvalds1da177e2005-04-16 15:20:36 -07003744static int patch_alc260(struct hda_codec *codec)
3745{
3746 struct alc_spec *spec;
Kailang Yangdf694da2005-12-05 19:42:22 +01003747 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003748
Takashi Iwaie560d8d2005-09-09 14:21:46 +02003749 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003750 if (spec == NULL)
3751 return -ENOMEM;
3752
3753 codec->spec = spec;
3754
Takashi Iwai16ded522005-06-10 19:58:24 +02003755 board_config = snd_hda_check_board_config(codec, alc260_cfg_tbl);
3756 if (board_config < 0 || board_config >= ALC260_MODEL_LAST) {
3757 snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260\n");
Kailang Yangdf694da2005-12-05 19:42:22 +01003758 board_config = ALC260_AUTO;
Takashi Iwai16ded522005-06-10 19:58:24 +02003759 }
3760
Kailang Yangdf694da2005-12-05 19:42:22 +01003761 if (board_config == ALC260_AUTO) {
3762 /* automatic parse from the BIOS config */
3763 err = alc260_parse_auto_config(codec);
3764 if (err < 0) {
3765 alc_free(codec);
3766 return err;
3767 } else if (! err) {
3768 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
3769 board_config = ALC260_BASIC;
3770 }
Takashi Iwai16ded522005-06-10 19:58:24 +02003771 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003772
Kailang Yangdf694da2005-12-05 19:42:22 +01003773 if (board_config != ALC260_AUTO)
3774 setup_preset(spec, &alc260_presets[board_config]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003775
3776 spec->stream_name_analog = "ALC260 Analog";
3777 spec->stream_analog_playback = &alc260_pcm_analog_playback;
3778 spec->stream_analog_capture = &alc260_pcm_analog_capture;
3779
Takashi Iwaia3bcba32005-12-06 19:05:29 +01003780 spec->stream_name_digital = "ALC260 Digital";
3781 spec->stream_digital_playback = &alc260_pcm_digital_playback;
3782 spec->stream_digital_capture = &alc260_pcm_digital_capture;
3783
Linus Torvalds1da177e2005-04-16 15:20:36 -07003784 codec->patch_ops = alc_patch_ops;
Kailang Yangdf694da2005-12-05 19:42:22 +01003785 if (board_config == ALC260_AUTO)
Takashi Iwaiae6b8132006-03-03 16:47:17 +01003786 spec->init_hook = alc260_auto_init;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003787
3788 return 0;
3789}
3790
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003791
Linus Torvalds1da177e2005-04-16 15:20:36 -07003792/*
3793 * ALC882 support
3794 *
3795 * ALC882 is almost identical with ALC880 but has cleaner and more flexible
3796 * configuration. Each pin widget can choose any input DACs and a mixer.
3797 * Each ADC is connected from a mixer of all inputs. This makes possible
3798 * 6-channel independent captures.
3799 *
3800 * In addition, an independent DAC for the multi-playback (not used in this
3801 * driver yet).
3802 */
Kailang Yangdf694da2005-12-05 19:42:22 +01003803#define ALC882_DIGOUT_NID 0x06
3804#define ALC882_DIGIN_NID 0x0a
Linus Torvalds1da177e2005-04-16 15:20:36 -07003805
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +01003806static struct hda_channel_mode alc882_ch_modes[1] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003807 { 8, NULL }
3808};
3809
3810static hda_nid_t alc882_dac_nids[4] = {
3811 /* front, rear, clfe, rear_surr */
3812 0x02, 0x03, 0x04, 0x05
3813};
3814
Kailang Yangdf694da2005-12-05 19:42:22 +01003815/* identical with ALC880 */
3816#define alc882_adc_nids alc880_adc_nids
3817#define alc882_adc_nids_alt alc880_adc_nids_alt
Linus Torvalds1da177e2005-04-16 15:20:36 -07003818
3819/* input MUX */
3820/* FIXME: should be a matrix-type input source selection */
3821
3822static struct hda_input_mux alc882_capture_source = {
3823 .num_items = 4,
3824 .items = {
3825 { "Mic", 0x0 },
3826 { "Front Mic", 0x1 },
3827 { "Line", 0x2 },
3828 { "CD", 0x4 },
3829 },
3830};
3831
3832#define alc882_mux_enum_info alc_mux_enum_info
3833#define alc882_mux_enum_get alc_mux_enum_get
3834
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01003835static int alc882_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003836{
3837 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3838 struct alc_spec *spec = codec->spec;
3839 const struct hda_input_mux *imux = spec->input_mux;
3840 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
3841 static hda_nid_t capture_mixers[3] = { 0x24, 0x23, 0x22 };
3842 hda_nid_t nid = capture_mixers[adc_idx];
3843 unsigned int *cur_val = &spec->cur_mux[adc_idx];
3844 unsigned int i, idx;
3845
3846 idx = ucontrol->value.enumerated.item[0];
3847 if (idx >= imux->num_items)
3848 idx = imux->num_items - 1;
3849 if (*cur_val == idx && ! codec->in_resume)
3850 return 0;
3851 for (i = 0; i < imux->num_items; i++) {
3852 unsigned int v = (i == idx) ? 0x7000 : 0x7080;
3853 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
3854 v | (imux->items[i].index << 8));
3855 }
3856 *cur_val = idx;
3857 return 1;
3858}
3859
Kailang Yangdf694da2005-12-05 19:42:22 +01003860/*
3861 * 6ch mode
3862 */
3863static struct hda_verb alc882_sixstack_ch6_init[] = {
3864 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
3865 { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
3866 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
3867 { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
3868 { } /* end */
3869};
3870
3871/*
3872 * 8ch mode
3873 */
3874static struct hda_verb alc882_sixstack_ch8_init[] = {
3875 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
3876 { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
3877 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
3878 { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
3879 { } /* end */
3880};
3881
3882static struct hda_channel_mode alc882_sixstack_modes[2] = {
3883 { 6, alc882_sixstack_ch6_init },
3884 { 8, alc882_sixstack_ch8_init },
3885};
3886
Linus Torvalds1da177e2005-04-16 15:20:36 -07003887/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
3888 * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
3889 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01003890static struct snd_kcontrol_new alc882_base_mixer[] = {
Takashi Iwai05acb862005-06-10 19:50:25 +02003891 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01003892 HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
Takashi Iwai05acb862005-06-10 19:50:25 +02003893 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01003894 HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
Takashi Iwai05acb862005-06-10 19:50:25 +02003895 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
3896 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01003897 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
3898 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
Takashi Iwai05acb862005-06-10 19:50:25 +02003899 HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai985be542005-11-02 18:26:49 +01003900 HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -07003901 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
3902 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
3903 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
3904 HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
3905 HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
3906 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
3907 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
3908 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
3909 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
3910 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
3911 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
3912 HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
3913 HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
3914 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
3915 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
3916 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
3917 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
3918 {
3919 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3920 /* .name = "Capture Source", */
3921 .name = "Input Source",
3922 .count = 3,
3923 .info = alc882_mux_enum_info,
3924 .get = alc882_mux_enum_get,
3925 .put = alc882_mux_enum_put,
3926 },
3927 { } /* end */
3928};
3929
Kailang Yangdf694da2005-12-05 19:42:22 +01003930static struct snd_kcontrol_new alc882_chmode_mixer[] = {
3931 {
3932 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3933 .name = "Channel Mode",
3934 .info = alc_ch_mode_info,
3935 .get = alc_ch_mode_get,
3936 .put = alc_ch_mode_put,
3937 },
3938 { } /* end */
3939};
3940
Linus Torvalds1da177e2005-04-16 15:20:36 -07003941static struct hda_verb alc882_init_verbs[] = {
3942 /* Front mixer: unmute input/output amp left and right (volume = 0) */
Takashi Iwai05acb862005-06-10 19:50:25 +02003943 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3944 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3945 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003946 /* Rear mixer */
Takashi Iwai05acb862005-06-10 19:50:25 +02003947 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3948 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3949 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003950 /* CLFE mixer */
Takashi Iwai05acb862005-06-10 19:50:25 +02003951 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3952 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3953 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003954 /* Side mixer */
Takashi Iwai05acb862005-06-10 19:50:25 +02003955 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3956 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3957 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003958
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003959 /* Front Pin: output 0 (0x0c) */
Takashi Iwai05acb862005-06-10 19:50:25 +02003960 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai05acb862005-06-10 19:50:25 +02003961 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003962 {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003963 /* Rear Pin: output 1 (0x0d) */
Takashi Iwai05acb862005-06-10 19:50:25 +02003964 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai05acb862005-06-10 19:50:25 +02003965 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003966 {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003967 /* CLFE Pin: output 2 (0x0e) */
Takashi Iwai05acb862005-06-10 19:50:25 +02003968 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai05acb862005-06-10 19:50:25 +02003969 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003970 {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003971 /* Side Pin: output 3 (0x0f) */
Takashi Iwai05acb862005-06-10 19:50:25 +02003972 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai05acb862005-06-10 19:50:25 +02003973 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003974 {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003975 /* Mic (rear) pin: input vref at 80% */
Takashi Iwai16ded522005-06-10 19:58:24 +02003976 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003977 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3978 /* Front Mic pin: input vref at 80% */
Takashi Iwai16ded522005-06-10 19:58:24 +02003979 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003980 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3981 /* Line In pin: input */
Takashi Iwai05acb862005-06-10 19:50:25 +02003982 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaie9edcee2005-06-13 14:16:38 +02003983 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3984 /* Line-2 In: Headphone output (output 0 - 0x0c) */
3985 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3986 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3987 {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003988 /* CD pin widget for input */
Takashi Iwai05acb862005-06-10 19:50:25 +02003989 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003990
3991 /* FIXME: use matrix-type input source selection */
3992 /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
3993 /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
Takashi Iwai05acb862005-06-10 19:50:25 +02003994 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3995 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3996 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3997 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07003998 /* Input mixer2 */
Takashi Iwai05acb862005-06-10 19:50:25 +02003999 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4000 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4001 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4002 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Linus Torvalds1da177e2005-04-16 15:20:36 -07004003 /* Input mixer3 */
Takashi Iwai05acb862005-06-10 19:50:25 +02004004 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4005 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4006 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4007 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4008 /* ADC1: mute amp left and right */
4009 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +02004010 {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwai05acb862005-06-10 19:50:25 +02004011 /* ADC2: mute amp left and right */
4012 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +02004013 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
Takashi Iwai05acb862005-06-10 19:50:25 +02004014 /* ADC3: mute amp left and right */
4015 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
Takashi Iwai71fe7b82005-05-25 18:11:40 +02004016 {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
Linus Torvalds1da177e2005-04-16 15:20:36 -07004017
4018 { }
4019};
4020
Kailang Yangdf694da2005-12-05 19:42:22 +01004021/*
4022 * generic initialization of ADC, input mixers and output mixers
4023 */
4024static struct hda_verb alc882_auto_init_verbs[] = {
4025 /*
4026 * Unmute ADC0-2 and set the default input to mic-in
4027 */
4028 {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
4029 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4030 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
4031 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4032 {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
4033 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4034
4035 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4036 * mixer widget
4037 * Note: PASD motherboards uses the Line In 2 as the input for front panel
4038 * mic (mic 2)
4039 */
4040 /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
4041 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4042 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4043 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
4044 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
4045 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
4046
4047 /*
4048 * Set up output mixers (0x0c - 0x0f)
4049 */
4050 /* set vol=0 to output mixers */
4051 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4052 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4053 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4054 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4055 /* set up input amps for analog loopback */
4056 /* Amp Indices: DAC = 0, mixer = 1 */
4057 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4058 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4059 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4060 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4061 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4062 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4063 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4064 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4065 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4066 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4067
4068 /* FIXME: use matrix-type input source selection */
4069 /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
4070 /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
4071 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
4072 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
4073 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
4074 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
4075 /* Input mixer2 */
4076 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
4077 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
4078 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
4079 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
4080 /* Input mixer3 */
4081 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
4082 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
4083 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
4084 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
4085
4086 { }
4087};
4088
4089/* capture mixer elements */
4090static struct snd_kcontrol_new alc882_capture_alt_mixer[] = {
4091 HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
4092 HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
4093 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
4094 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
4095 {
4096 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4097 /* The multiple "Capture Source" controls confuse alsamixer
4098 * So call somewhat different..
4099 * FIXME: the controls appear in the "playback" view!
4100 */
4101 /* .name = "Capture Source", */
4102 .name = "Input Source",
4103 .count = 2,
4104 .info = alc882_mux_enum_info,
4105 .get = alc882_mux_enum_get,
4106 .put = alc882_mux_enum_put,
4107 },
4108 { } /* end */
4109};
4110
4111static struct snd_kcontrol_new alc882_capture_mixer[] = {
4112 HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
4113 HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
4114 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
4115 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
4116 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
4117 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
4118 {
4119 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4120 /* The multiple "Capture Source" controls confuse alsamixer
4121 * So call somewhat different..
4122 * FIXME: the controls appear in the "playback" view!
4123 */
4124 /* .name = "Capture Source", */
4125 .name = "Input Source",
4126 .count = 3,
4127 .info = alc882_mux_enum_info,
4128 .get = alc882_mux_enum_get,
4129 .put = alc882_mux_enum_put,
4130 },
4131 { } /* end */
4132};
4133
4134/* pcm configuration: identiacal with ALC880 */
4135#define alc882_pcm_analog_playback alc880_pcm_analog_playback
4136#define alc882_pcm_analog_capture alc880_pcm_analog_capture
4137#define alc882_pcm_digital_playback alc880_pcm_digital_playback
4138#define alc882_pcm_digital_capture alc880_pcm_digital_capture
4139
4140/*
4141 * configuration and preset
4142 */
4143static struct hda_board_config alc882_cfg_tbl[] = {
Takashi Iwai1494a922006-01-31 10:58:46 +01004144 { .modelname = "3stack-dig", .config = ALC882_3ST_DIG },
4145 { .modelname = "6stack-dig", .config = ALC882_6ST_DIG },
Kailang Yangdf694da2005-12-05 19:42:22 +01004146 { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* MSI */
4147 { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* Foxconn */
4148 { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* ECS */
Takashi Iwai1494a922006-01-31 10:58:46 +01004149 { .modelname = "auto", .config = ALC882_AUTO },
Kailang Yangdf694da2005-12-05 19:42:22 +01004150 {}
4151};
4152
4153static struct alc_config_preset alc882_presets[] = {
4154 [ALC882_3ST_DIG] = {
4155 .mixers = { alc882_base_mixer },
4156 .init_verbs = { alc882_init_verbs },
4157 .num_dacs = ARRAY_SIZE(alc882_dac_nids),
4158 .dac_nids = alc882_dac_nids,
4159 .dig_out_nid = ALC882_DIGOUT_NID,
4160 .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
4161 .adc_nids = alc882_adc_nids,
4162 .dig_in_nid = ALC882_DIGIN_NID,
4163 .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
4164 .channel_mode = alc882_ch_modes,
4165 .input_mux = &alc882_capture_source,
4166 },
4167 [ALC882_6ST_DIG] = {
4168 .mixers = { alc882_base_mixer, alc882_chmode_mixer },
4169 .init_verbs = { alc882_init_verbs },
4170 .num_dacs = ARRAY_SIZE(alc882_dac_nids),
4171 .dac_nids = alc882_dac_nids,
4172 .dig_out_nid = ALC882_DIGOUT_NID,
4173 .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
4174 .adc_nids = alc882_adc_nids,
4175 .dig_in_nid = ALC882_DIGIN_NID,
4176 .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
4177 .channel_mode = alc882_sixstack_modes,
4178 .input_mux = &alc882_capture_source,
4179 },
4180};
4181
4182
4183/*
4184 * BIOS auto configuration
4185 */
4186static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
4187 hda_nid_t nid, int pin_type,
4188 int dac_idx)
4189{
4190 /* set as output */
4191 struct alc_spec *spec = codec->spec;
4192 int idx;
4193
4194 if (spec->multiout.dac_nids[dac_idx] == 0x25)
4195 idx = 4;
4196 else
4197 idx = spec->multiout.dac_nids[dac_idx] - 2;
4198
4199 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
4200 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
4201 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
4202
4203}
4204
4205static void alc882_auto_init_multi_out(struct hda_codec *codec)
4206{
4207 struct alc_spec *spec = codec->spec;
4208 int i;
4209
4210 for (i = 0; i <= HDA_SIDE; i++) {
4211 hda_nid_t nid = spec->autocfg.line_out_pins[i];
4212 if (nid)
4213 alc882_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
4214 }
4215}
4216
4217static void alc882_auto_init_hp_out(struct hda_codec *codec)
4218{
4219 struct alc_spec *spec = codec->spec;
4220 hda_nid_t pin;
4221
4222 pin = spec->autocfg.hp_pin;
4223 if (pin) /* connect to front */
4224 alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); /* use dac 0 */
4225}
4226
4227#define alc882_is_input_pin(nid) alc880_is_input_pin(nid)
4228#define ALC882_PIN_CD_NID ALC880_PIN_CD_NID
4229
4230static void alc882_auto_init_analog_input(struct hda_codec *codec)
4231{
4232 struct alc_spec *spec = codec->spec;
4233 int i;
4234
4235 for (i = 0; i < AUTO_PIN_LAST; i++) {
4236 hda_nid_t nid = spec->autocfg.input_pins[i];
4237 if (alc882_is_input_pin(nid)) {
4238 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
4239 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
4240 if (nid != ALC882_PIN_CD_NID)
4241 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
4242 AMP_OUT_MUTE);
4243 }
4244 }
4245}
4246
4247/* almost identical with ALC880 parser... */
4248static int alc882_parse_auto_config(struct hda_codec *codec)
4249{
4250 struct alc_spec *spec = codec->spec;
4251 int err = alc880_parse_auto_config(codec);
4252
4253 if (err < 0)
4254 return err;
Takashi Iwaic5f2ea02005-12-06 18:54:31 +01004255 else if (err > 0)
4256 /* hack - override the init verbs */
4257 spec->init_verbs[0] = alc882_auto_init_verbs;
4258 return err;
Kailang Yangdf694da2005-12-05 19:42:22 +01004259}
4260
Takashi Iwaiae6b8132006-03-03 16:47:17 +01004261/* additional initialization for auto-configuration model */
4262static void alc882_auto_init(struct hda_codec *codec)
Kailang Yangdf694da2005-12-05 19:42:22 +01004263{
Kailang Yangdf694da2005-12-05 19:42:22 +01004264 alc882_auto_init_multi_out(codec);
4265 alc882_auto_init_hp_out(codec);
4266 alc882_auto_init_analog_input(codec);
Kailang Yangdf694da2005-12-05 19:42:22 +01004267}
4268
4269/*
4270 * ALC882 Headphone poll in 3.5.1a or 3.5.2
4271 */
4272
Linus Torvalds1da177e2005-04-16 15:20:36 -07004273static int patch_alc882(struct hda_codec *codec)
4274{
4275 struct alc_spec *spec;
Kailang Yangdf694da2005-12-05 19:42:22 +01004276 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004277
Takashi Iwaie560d8d2005-09-09 14:21:46 +02004278 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004279 if (spec == NULL)
4280 return -ENOMEM;
4281
Linus Torvalds1da177e2005-04-16 15:20:36 -07004282 codec->spec = spec;
4283
Kailang Yangdf694da2005-12-05 19:42:22 +01004284 board_config = snd_hda_check_board_config(codec, alc882_cfg_tbl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004285
Kailang Yangdf694da2005-12-05 19:42:22 +01004286 if (board_config < 0 || board_config >= ALC882_MODEL_LAST) {
4287 printk(KERN_INFO "hda_codec: Unknown model for ALC882, trying auto-probe from BIOS...\n");
4288 board_config = ALC882_AUTO;
4289 }
Takashi Iwaie9edcee2005-06-13 14:16:38 +02004290
Kailang Yangdf694da2005-12-05 19:42:22 +01004291 if (board_config == ALC882_AUTO) {
4292 /* automatic parse from the BIOS config */
4293 err = alc882_parse_auto_config(codec);
4294 if (err < 0) {
4295 alc_free(codec);
4296 return err;
4297 } else if (! err) {
4298 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
4299 board_config = ALC882_3ST_DIG;
4300 }
4301 }
4302
4303 if (board_config != ALC882_AUTO)
4304 setup_preset(spec, &alc882_presets[board_config]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004305
4306 spec->stream_name_analog = "ALC882 Analog";
Kailang Yangdf694da2005-12-05 19:42:22 +01004307 spec->stream_analog_playback = &alc882_pcm_analog_playback;
4308 spec->stream_analog_capture = &alc882_pcm_analog_capture;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004309
4310 spec->stream_name_digital = "ALC882 Digital";
Kailang Yangdf694da2005-12-05 19:42:22 +01004311 spec->stream_digital_playback = &alc882_pcm_digital_playback;
4312 spec->stream_digital_capture = &alc882_pcm_digital_capture;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004313
Kailang Yangdf694da2005-12-05 19:42:22 +01004314 if (! spec->adc_nids && spec->input_mux) {
4315 /* check whether NID 0x07 is valid */
Takashi Iwai4a471b72005-12-07 13:56:29 +01004316 unsigned int wcap = get_wcaps(codec, 0x07);
Kailang Yangdf694da2005-12-05 19:42:22 +01004317 wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
4318 if (wcap != AC_WID_AUD_IN) {
4319 spec->adc_nids = alc882_adc_nids_alt;
4320 spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt);
4321 spec->mixers[spec->num_mixers] = alc882_capture_alt_mixer;
4322 spec->num_mixers++;
4323 } else {
4324 spec->adc_nids = alc882_adc_nids;
4325 spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids);
4326 spec->mixers[spec->num_mixers] = alc882_capture_mixer;
4327 spec->num_mixers++;
4328 }
4329 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004330
4331 codec->patch_ops = alc_patch_ops;
Kailang Yangdf694da2005-12-05 19:42:22 +01004332 if (board_config == ALC882_AUTO)
Takashi Iwaiae6b8132006-03-03 16:47:17 +01004333 spec->init_hook = alc882_auto_init;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004334
4335 return 0;
4336}
4337
4338/*
Kailang Yangdf694da2005-12-05 19:42:22 +01004339 * ALC262 support
4340 */
4341
4342#define ALC262_DIGOUT_NID ALC880_DIGOUT_NID
4343#define ALC262_DIGIN_NID ALC880_DIGIN_NID
4344
4345#define alc262_dac_nids alc260_dac_nids
4346#define alc262_adc_nids alc882_adc_nids
4347#define alc262_adc_nids_alt alc882_adc_nids_alt
4348
4349#define alc262_modes alc260_modes
Takashi Iwaic5f2ea02005-12-06 18:54:31 +01004350#define alc262_capture_source alc882_capture_source
Kailang Yangdf694da2005-12-05 19:42:22 +01004351
4352static struct snd_kcontrol_new alc262_base_mixer[] = {
4353 HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
4354 HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
4355 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
4356 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
4357 HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
4358 HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
4359 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
4360 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
4361 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
4362 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
4363 /* HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT),
4364 HDA_CODEC_MUTE("PC Beelp Playback Switch", 0x0b, 0x05, HDA_INPUT), */
4365 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),
4366 HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4367 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
4368 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
Kailang Yangdf694da2005-12-05 19:42:22 +01004369 { } /* end */
Takashi Iwai834be882006-03-01 14:16:17 +01004370};
4371
Kailang Yangdf694da2005-12-05 19:42:22 +01004372#define alc262_capture_mixer alc882_capture_mixer
4373#define alc262_capture_alt_mixer alc882_capture_alt_mixer
4374
4375/*
4376 * generic initialization of ADC, input mixers and output mixers
4377 */
4378static struct hda_verb alc262_init_verbs[] = {
4379 /*
4380 * Unmute ADC0-2 and set the default input to mic-in
4381 */
4382 {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
4383 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4384 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
4385 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4386 {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
4387 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4388
4389 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4390 * mixer widget
4391 * Note: PASD motherboards uses the Line In 2 as the input for front panel
4392 * mic (mic 2)
4393 */
4394 /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
4395 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4396 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4397 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
4398 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
4399 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
4400
4401 /*
4402 * Set up output mixers (0x0c - 0x0e)
4403 */
4404 /* set vol=0 to output mixers */
4405 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4406 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4407 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4408 /* set up input amps for analog loopback */
4409 /* Amp Indices: DAC = 0, mixer = 1 */
4410 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4411 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4412 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4413 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4414 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4415 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4416
4417 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4418 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
4419 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4420 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
4421 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
4422 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
4423
4424 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
4425 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
4426 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
4427 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
4428 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
4429
4430 {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
4431 {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
4432
4433 /* FIXME: use matrix-type input source selection */
4434 /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
4435 /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
4436 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
4437 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
4438 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
4439 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
4440 /* Input mixer2 */
4441 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
4442 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
4443 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
4444 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
4445 /* Input mixer3 */
4446 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
4447 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
4448 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
4449 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
4450
4451 { }
4452};
4453
Takashi Iwai834be882006-03-01 14:16:17 +01004454/*
4455 * fujitsu model
4456 * 0x14 = headphone/spdif-out, 0x15 = internal speaker
4457 */
4458
4459#define ALC_HP_EVENT 0x37
4460
4461static struct hda_verb alc262_fujitsu_unsol_verbs[] = {
4462 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
4463 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4464 {}
4465};
4466
4467static struct hda_input_mux alc262_fujitsu_capture_source = {
4468 .num_items = 2,
4469 .items = {
4470 { "Mic", 0x0 },
4471 { "CD", 0x4 },
4472 },
4473};
4474
4475/* mute/unmute internal speaker according to the hp jack and mute state */
4476static void alc262_fujitsu_automute(struct hda_codec *codec, int force)
4477{
4478 struct alc_spec *spec = codec->spec;
4479 unsigned int mute;
4480
4481 if (force || ! spec->sense_updated) {
4482 unsigned int present;
4483 /* need to execute and sync at first */
4484 snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0);
4485 present = snd_hda_codec_read(codec, 0x14, 0,
4486 AC_VERB_GET_PIN_SENSE, 0);
4487 spec->jack_present = (present & 0x80000000) != 0;
4488 spec->sense_updated = 1;
4489 }
4490 if (spec->jack_present) {
4491 /* mute internal speaker */
4492 snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0,
4493 0x80, 0x80);
4494 snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0,
4495 0x80, 0x80);
4496 } else {
4497 /* unmute internal speaker if necessary */
4498 mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
4499 snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0,
4500 0x80, mute & 0x80);
4501 mute = snd_hda_codec_amp_read(codec, 0x14, 1, HDA_OUTPUT, 0);
4502 snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0,
4503 0x80, mute & 0x80);
4504 }
4505}
4506
4507/* unsolicited event for HP jack sensing */
4508static void alc262_fujitsu_unsol_event(struct hda_codec *codec,
4509 unsigned int res)
4510{
4511 if ((res >> 26) != ALC_HP_EVENT)
4512 return;
4513 alc262_fujitsu_automute(codec, 1);
4514}
4515
4516/* bind volumes of both NID 0x0c and 0x0d */
4517static int alc262_fujitsu_master_vol_put(struct snd_kcontrol *kcontrol,
4518 struct snd_ctl_elem_value *ucontrol)
4519{
4520 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4521 long *valp = ucontrol->value.integer.value;
4522 int change;
4523
4524 change = snd_hda_codec_amp_update(codec, 0x0c, 0, HDA_OUTPUT, 0,
4525 0x7f, valp[0] & 0x7f);
4526 change |= snd_hda_codec_amp_update(codec, 0x0c, 1, HDA_OUTPUT, 0,
4527 0x7f, valp[1] & 0x7f);
4528 snd_hda_codec_amp_update(codec, 0x0d, 0, HDA_OUTPUT, 0,
4529 0x7f, valp[0] & 0x7f);
4530 snd_hda_codec_amp_update(codec, 0x0d, 1, HDA_OUTPUT, 0,
4531 0x7f, valp[1] & 0x7f);
4532 return change;
4533}
4534
4535/* bind hp and internal speaker mute (with plug check) */
4536static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol,
4537 struct snd_ctl_elem_value *ucontrol)
4538{
4539 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4540 long *valp = ucontrol->value.integer.value;
4541 int change;
4542
4543 change = snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0,
4544 0x80, valp[0] ? 0 : 0x80);
4545 change |= snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0,
4546 0x80, valp[1] ? 0 : 0x80);
4547 if (change || codec->in_resume)
4548 alc262_fujitsu_automute(codec, codec->in_resume);
4549 return change;
4550}
4551
4552static struct snd_kcontrol_new alc262_fujitsu_mixer[] = {
4553 {
4554 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4555 .name = "Master Playback Volume",
4556 .info = snd_hda_mixer_amp_volume_info,
4557 .get = snd_hda_mixer_amp_volume_get,
4558 .put = alc262_fujitsu_master_vol_put,
4559 .private_value = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT),
4560 },
4561 {
4562 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4563 .name = "Master Playback Switch",
4564 .info = snd_hda_mixer_amp_switch_info,
4565 .get = snd_hda_mixer_amp_switch_get,
4566 .put = alc262_fujitsu_master_sw_put,
4567 .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
4568 },
4569 HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
4570 HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
4571 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
4572 HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
4573 HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
4574 { } /* end */
4575};
4576
Kailang Yangdf694da2005-12-05 19:42:22 +01004577/* add playback controls from the parsed DAC table */
4578static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
4579{
4580 hda_nid_t nid;
4581 int err;
4582
4583 spec->multiout.num_dacs = 1; /* only use one dac */
4584 spec->multiout.dac_nids = spec->private_dac_nids;
4585 spec->multiout.dac_nids[0] = 2;
4586
4587 nid = cfg->line_out_pins[0];
4588 if (nid) {
4589 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Front Playback Volume",
4590 HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT))) < 0)
4591 return err;
4592 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Front Playback Switch",
4593 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
4594 return err;
4595 }
4596
Takashi Iwai82bc9552006-03-21 11:24:42 +01004597 nid = cfg->speaker_pins[0];
Kailang Yangdf694da2005-12-05 19:42:22 +01004598 if (nid) {
4599 if (nid == 0x16) {
4600 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Speaker Playback Volume",
4601 HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT))) < 0)
4602 return err;
4603 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Speaker Playback Switch",
4604 HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
4605 return err;
4606 } else {
Kailang Yangdf694da2005-12-05 19:42:22 +01004607 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Speaker Playback Switch",
4608 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
4609 return err;
4610 }
4611 }
4612 nid = cfg->hp_pin;
4613 if (nid) {
4614 /* spec->multiout.hp_nid = 2; */
4615 if (nid == 0x16) {
4616 if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Headphone Playback Volume",
4617 HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT))) < 0)
4618 return err;
4619 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
4620 HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
4621 return err;
4622 } else {
Kailang Yangdf694da2005-12-05 19:42:22 +01004623 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
4624 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
4625 return err;
4626 }
4627 }
4628 return 0;
4629}
4630
4631/* identical with ALC880 */
4632#define alc262_auto_create_analog_input_ctls alc880_auto_create_analog_input_ctls
4633
4634/*
4635 * generic initialization of ADC, input mixers and output mixers
4636 */
4637static struct hda_verb alc262_volume_init_verbs[] = {
4638 /*
4639 * Unmute ADC0-2 and set the default input to mic-in
4640 */
4641 {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
4642 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4643 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
4644 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4645 {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
4646 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4647
4648 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4649 * mixer widget
4650 * Note: PASD motherboards uses the Line In 2 as the input for front panel
4651 * mic (mic 2)
4652 */
4653 /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
4654 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4655 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4656 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
4657 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
4658 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
4659
4660 /*
4661 * Set up output mixers (0x0c - 0x0f)
4662 */
4663 /* set vol=0 to output mixers */
4664 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4665 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4666 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4667
4668 /* set up input amps for analog loopback */
4669 /* Amp Indices: DAC = 0, mixer = 1 */
4670 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4671 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4672 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4673 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4674 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4675 {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4676
4677 /* FIXME: use matrix-type input source selection */
4678 /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
4679 /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
4680 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
4681 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
4682 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
4683 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
4684 /* Input mixer2 */
4685 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
4686 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
4687 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
4688 {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
4689 /* Input mixer3 */
4690 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
4691 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
4692 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
4693 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
4694
4695 { }
4696};
4697
4698/* pcm configuration: identiacal with ALC880 */
4699#define alc262_pcm_analog_playback alc880_pcm_analog_playback
4700#define alc262_pcm_analog_capture alc880_pcm_analog_capture
4701#define alc262_pcm_digital_playback alc880_pcm_digital_playback
4702#define alc262_pcm_digital_capture alc880_pcm_digital_capture
4703
4704/*
4705 * BIOS auto configuration
4706 */
4707static int alc262_parse_auto_config(struct hda_codec *codec)
4708{
4709 struct alc_spec *spec = codec->spec;
4710 int err;
4711 static hda_nid_t alc262_ignore[] = { 0x1d, 0 };
4712
4713 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
4714 alc262_ignore)) < 0)
4715 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01004716 if (! spec->autocfg.line_outs)
Kailang Yangdf694da2005-12-05 19:42:22 +01004717 return 0; /* can't find valid BIOS pin config */
4718 if ((err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
4719 (err = alc262_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
4720 return err;
4721
4722 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4723
4724 if (spec->autocfg.dig_out_pin)
4725 spec->multiout.dig_out_nid = ALC262_DIGOUT_NID;
4726 if (spec->autocfg.dig_in_pin)
4727 spec->dig_in_nid = ALC262_DIGIN_NID;
4728
4729 if (spec->kctl_alloc)
4730 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
4731
4732 spec->init_verbs[spec->num_init_verbs++] = alc262_volume_init_verbs;
4733 spec->input_mux = &spec->private_imux;
4734
4735 return 1;
4736}
4737
4738#define alc262_auto_init_multi_out alc882_auto_init_multi_out
4739#define alc262_auto_init_hp_out alc882_auto_init_hp_out
4740#define alc262_auto_init_analog_input alc882_auto_init_analog_input
4741
4742
4743/* init callback for auto-configuration model -- overriding the default init */
Takashi Iwaiae6b8132006-03-03 16:47:17 +01004744static void alc262_auto_init(struct hda_codec *codec)
Kailang Yangdf694da2005-12-05 19:42:22 +01004745{
Kailang Yangdf694da2005-12-05 19:42:22 +01004746 alc262_auto_init_multi_out(codec);
4747 alc262_auto_init_hp_out(codec);
4748 alc262_auto_init_analog_input(codec);
Kailang Yangdf694da2005-12-05 19:42:22 +01004749}
4750
4751/*
4752 * configuration and preset
4753 */
4754static struct hda_board_config alc262_cfg_tbl[] = {
4755 { .modelname = "basic", .config = ALC262_BASIC },
Takashi Iwai834be882006-03-01 14:16:17 +01004756 { .modelname = "fujitsu", .config = ALC262_FUJITSU },
4757 { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397, .config = ALC262_FUJITSU },
Kailang Yangdf694da2005-12-05 19:42:22 +01004758 { .modelname = "auto", .config = ALC262_AUTO },
4759 {}
4760};
4761
4762static struct alc_config_preset alc262_presets[] = {
4763 [ALC262_BASIC] = {
4764 .mixers = { alc262_base_mixer },
4765 .init_verbs = { alc262_init_verbs },
4766 .num_dacs = ARRAY_SIZE(alc262_dac_nids),
4767 .dac_nids = alc262_dac_nids,
4768 .hp_nid = 0x03,
4769 .num_channel_mode = ARRAY_SIZE(alc262_modes),
4770 .channel_mode = alc262_modes,
Takashi Iwaia3bcba32005-12-06 19:05:29 +01004771 .input_mux = &alc262_capture_source,
Kailang Yangdf694da2005-12-05 19:42:22 +01004772 },
Takashi Iwai834be882006-03-01 14:16:17 +01004773 [ALC262_FUJITSU] = {
4774 .mixers = { alc262_fujitsu_mixer },
4775 .init_verbs = { alc262_init_verbs, alc262_fujitsu_unsol_verbs },
4776 .num_dacs = ARRAY_SIZE(alc262_dac_nids),
4777 .dac_nids = alc262_dac_nids,
4778 .hp_nid = 0x03,
4779 .dig_out_nid = ALC262_DIGOUT_NID,
4780 .num_channel_mode = ARRAY_SIZE(alc262_modes),
4781 .channel_mode = alc262_modes,
4782 .input_mux = &alc262_fujitsu_capture_source,
Takashi Iwaiae6b8132006-03-03 16:47:17 +01004783 .unsol_event = alc262_fujitsu_unsol_event,
Takashi Iwai834be882006-03-01 14:16:17 +01004784 },
Kailang Yangdf694da2005-12-05 19:42:22 +01004785};
4786
4787static int patch_alc262(struct hda_codec *codec)
4788{
4789 struct alc_spec *spec;
4790 int board_config;
4791 int err;
4792
4793 spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
4794 if (spec == NULL)
4795 return -ENOMEM;
4796
4797 codec->spec = spec;
4798#if 0
4799 /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is under-run */
4800 {
4801 int tmp;
4802 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
4803 tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
4804 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
4805 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80);
4806 }
4807#endif
4808
4809 board_config = snd_hda_check_board_config(codec, alc262_cfg_tbl);
4810 if (board_config < 0 || board_config >= ALC262_MODEL_LAST) {
4811 printk(KERN_INFO "hda_codec: Unknown model for ALC262, trying auto-probe from BIOS...\n");
4812 board_config = ALC262_AUTO;
4813 }
4814
4815 if (board_config == ALC262_AUTO) {
4816 /* automatic parse from the BIOS config */
4817 err = alc262_parse_auto_config(codec);
4818 if (err < 0) {
4819 alc_free(codec);
4820 return err;
4821 } else if (! err) {
4822 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
4823 board_config = ALC262_BASIC;
4824 }
4825 }
4826
4827 if (board_config != ALC262_AUTO)
4828 setup_preset(spec, &alc262_presets[board_config]);
4829
4830 spec->stream_name_analog = "ALC262 Analog";
4831 spec->stream_analog_playback = &alc262_pcm_analog_playback;
4832 spec->stream_analog_capture = &alc262_pcm_analog_capture;
4833
4834 spec->stream_name_digital = "ALC262 Digital";
4835 spec->stream_digital_playback = &alc262_pcm_digital_playback;
4836 spec->stream_digital_capture = &alc262_pcm_digital_capture;
4837
4838 if (! spec->adc_nids && spec->input_mux) {
4839 /* check whether NID 0x07 is valid */
Takashi Iwai4a471b72005-12-07 13:56:29 +01004840 unsigned int wcap = get_wcaps(codec, 0x07);
4841
Kailang Yangdf694da2005-12-05 19:42:22 +01004842 wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
4843 if (wcap != AC_WID_AUD_IN) {
4844 spec->adc_nids = alc262_adc_nids_alt;
4845 spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt);
4846 spec->mixers[spec->num_mixers] = alc262_capture_alt_mixer;
4847 spec->num_mixers++;
4848 } else {
4849 spec->adc_nids = alc262_adc_nids;
4850 spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids);
4851 spec->mixers[spec->num_mixers] = alc262_capture_mixer;
4852 spec->num_mixers++;
4853 }
4854 }
4855
4856 codec->patch_ops = alc_patch_ops;
4857 if (board_config == ALC262_AUTO)
Takashi Iwaiae6b8132006-03-03 16:47:17 +01004858 spec->init_hook = alc262_auto_init;
Takashi Iwai834be882006-03-01 14:16:17 +01004859
Kailang Yangdf694da2005-12-05 19:42:22 +01004860 return 0;
4861}
4862
4863
4864/*
4865 * ALC861 channel source setting (2/6 channel selection for 3-stack)
4866 */
4867
4868/*
4869 * set the path ways for 2 channel output
4870 * need to set the codec line out and mic 1 pin widgets to inputs
4871 */
4872static struct hda_verb alc861_threestack_ch2_init[] = {
4873 /* set pin widget 1Ah (line in) for input */
4874 { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
4875 /* set pin widget 18h (mic1/2) for input, for mic also enable the vref */
4876 { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
4877
4878 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
4879 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, //mic
4880 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, //line in
4881 { } /* end */
4882};
4883/*
4884 * 6ch mode
4885 * need to set the codec line out and mic 1 pin widgets to outputs
4886 */
4887static struct hda_verb alc861_threestack_ch6_init[] = {
4888 /* set pin widget 1Ah (line in) for output (Back Surround)*/
4889 { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
4890 /* set pin widget 18h (mic1) for output (CLFE)*/
4891 { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
4892
4893 { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 },
4894 { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
4895
4896 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
4897 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, //mic
4898 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, //line in
4899 { } /* end */
4900};
4901
4902static struct hda_channel_mode alc861_threestack_modes[2] = {
4903 { 2, alc861_threestack_ch2_init },
4904 { 6, alc861_threestack_ch6_init },
4905};
4906
4907/* patch-ALC861 */
4908
4909static struct snd_kcontrol_new alc861_base_mixer[] = {
4910 /* output mixer control */
4911 HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
4912 HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
4913 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
4914 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
4915 HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT),
4916
4917 /*Input mixer control */
4918 /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
4919 HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
4920 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
4921 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
4922 HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
4923 HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
4924 HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
4925 HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
4926 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
4927 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
4928
4929 /* Capture mixer control */
4930 HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
4931 HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
4932 {
4933 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4934 .name = "Capture Source",
4935 .count = 1,
4936 .info = alc_mux_enum_info,
4937 .get = alc_mux_enum_get,
4938 .put = alc_mux_enum_put,
4939 },
4940 { } /* end */
4941};
4942
4943static struct snd_kcontrol_new alc861_3ST_mixer[] = {
4944 /* output mixer control */
4945 HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
4946 HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
4947 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
4948 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
4949 /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */
4950
4951 /* Input mixer control */
4952 /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
4953 HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
4954 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
4955 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
4956 HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
4957 HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
4958 HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
4959 HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
4960 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
4961 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
4962
4963 /* Capture mixer control */
4964 HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
4965 HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
4966 {
4967 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4968 .name = "Capture Source",
4969 .count = 1,
4970 .info = alc_mux_enum_info,
4971 .get = alc_mux_enum_get,
4972 .put = alc_mux_enum_put,
4973 },
4974 {
4975 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4976 .name = "Channel Mode",
4977 .info = alc_ch_mode_info,
4978 .get = alc_ch_mode_get,
4979 .put = alc_ch_mode_put,
4980 .private_value = ARRAY_SIZE(alc861_threestack_modes),
4981 },
4982 { } /* end */
4983};
4984
4985/*
4986 * generic initialization of ADC, input mixers and output mixers
4987 */
4988static struct hda_verb alc861_base_init_verbs[] = {
4989 /*
4990 * Unmute ADC0 and set the default input to mic-in
4991 */
4992 /* port-A for surround (rear panel) */
4993 { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
4994 { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x00 },
4995 /* port-B for mic-in (rear panel) with vref */
4996 { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
4997 /* port-C for line-in (rear panel) */
4998 { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
4999 /* port-D for Front */
5000 { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
5001 { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
5002 /* port-E for HP out (front panel) */
5003 { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
5004 /* route front PCM to HP */
5005 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 },
5006 /* port-F for mic-in (front panel) with vref */
5007 { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
5008 /* port-G for CLFE (rear panel) */
5009 { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
5010 { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x00 },
5011 /* port-H for side (rear panel) */
5012 { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
5013 { 0x20, AC_VERB_SET_CONNECT_SEL, 0x00 },
5014 /* CD-in */
5015 { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
5016 /* route front mic to ADC1*/
5017 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
5018 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5019
5020 /* Unmute DAC0~3 & spdif out*/
5021 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
5022 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
5023 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
5024 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
5025 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
5026
5027 /* Unmute Mixer 14 (mic) 1c (Line in)*/
5028 {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5029 {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5030 {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5031 {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5032
5033 /* Unmute Stereo Mixer 15 */
5034 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5035 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5036 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
5037 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, //Output 0~12 step
5038
5039 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5040 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5041 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5042 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5043 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5044 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5045 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5046 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5047 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, // hp used DAC 3 (Front)
5048 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
5049
5050 { }
5051};
5052
5053static struct hda_verb alc861_threestack_init_verbs[] = {
5054 /*
5055 * Unmute ADC0 and set the default input to mic-in
5056 */
5057 /* port-A for surround (rear panel) */
5058 { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
5059 /* port-B for mic-in (rear panel) with vref */
5060 { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
5061 /* port-C for line-in (rear panel) */
5062 { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
5063 /* port-D for Front */
5064 { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
5065 { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
5066 /* port-E for HP out (front panel) */
5067 { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
5068 /* route front PCM to HP */
5069 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 },
5070 /* port-F for mic-in (front panel) with vref */
5071 { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
5072 /* port-G for CLFE (rear panel) */
5073 { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
5074 /* port-H for side (rear panel) */
5075 { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
5076 /* CD-in */
5077 { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
5078 /* route front mic to ADC1*/
5079 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
5080 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5081 /* Unmute DAC0~3 & spdif out*/
5082 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
5083 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
5084 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
5085 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
5086 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
5087
5088 /* Unmute Mixer 14 (mic) 1c (Line in)*/
5089 {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5090 {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5091 {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5092 {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5093
5094 /* Unmute Stereo Mixer 15 */
5095 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5096 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5097 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
5098 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, //Output 0~12 step
5099
5100 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5101 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5102 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5103 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5104 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5105 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5106 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5107 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5108 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, // hp used DAC 3 (Front)
5109 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
5110 { }
5111};
5112/*
5113 * generic initialization of ADC, input mixers and output mixers
5114 */
5115static struct hda_verb alc861_auto_init_verbs[] = {
5116 /*
5117 * Unmute ADC0 and set the default input to mic-in
5118 */
5119// {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
5120 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5121
5122 /* Unmute DAC0~3 & spdif out*/
5123 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5124 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5125 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5126 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5127 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
5128
5129 /* Unmute Mixer 14 (mic) 1c (Line in)*/
5130 {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5131 {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5132 {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5133 {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5134
5135 /* Unmute Stereo Mixer 15 */
5136 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5137 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5138 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
5139 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c},
5140
5141 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5142 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5143 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5144 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5145 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5146 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5147 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5148 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5149
5150 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5151 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5152 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
5153 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
5154 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5155 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5156 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
5157 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
5158
5159 {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, // set Mic 1
5160
5161 { }
5162};
5163
5164/* pcm configuration: identiacal with ALC880 */
5165#define alc861_pcm_analog_playback alc880_pcm_analog_playback
5166#define alc861_pcm_analog_capture alc880_pcm_analog_capture
5167#define alc861_pcm_digital_playback alc880_pcm_digital_playback
5168#define alc861_pcm_digital_capture alc880_pcm_digital_capture
5169
5170
5171#define ALC861_DIGOUT_NID 0x07
5172
5173static struct hda_channel_mode alc861_8ch_modes[1] = {
5174 { 8, NULL }
5175};
5176
5177static hda_nid_t alc861_dac_nids[4] = {
5178 /* front, surround, clfe, side */
5179 0x03, 0x06, 0x05, 0x04
5180};
5181
5182static hda_nid_t alc861_adc_nids[1] = {
5183 /* ADC0-2 */
5184 0x08,
5185};
5186
5187static struct hda_input_mux alc861_capture_source = {
5188 .num_items = 5,
5189 .items = {
5190 { "Mic", 0x0 },
5191 { "Front Mic", 0x3 },
5192 { "Line", 0x1 },
5193 { "CD", 0x4 },
5194 { "Mixer", 0x5 },
5195 },
5196};
5197
5198/* fill in the dac_nids table from the parsed pin configuration */
5199static int alc861_auto_fill_dac_nids(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
5200{
5201 int i;
5202 hda_nid_t nid;
5203
5204 spec->multiout.dac_nids = spec->private_dac_nids;
5205 for (i = 0; i < cfg->line_outs; i++) {
5206 nid = cfg->line_out_pins[i];
5207 if (nid) {
5208 if (i >= ARRAY_SIZE(alc861_dac_nids))
5209 continue;
5210 spec->multiout.dac_nids[i] = alc861_dac_nids[i];
5211 }
5212 }
5213 spec->multiout.num_dacs = cfg->line_outs;
5214 return 0;
5215}
5216
5217/* add playback controls from the parsed DAC table */
5218static int alc861_auto_create_multi_out_ctls(struct alc_spec *spec,
5219 const struct auto_pin_cfg *cfg)
5220{
5221 char name[32];
5222 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
5223 hda_nid_t nid;
5224 int i, idx, err;
5225
5226 for (i = 0; i < cfg->line_outs; i++) {
5227 nid = spec->multiout.dac_nids[i];
5228 if (! nid)
5229 continue;
5230 if (nid == 0x05) {
5231 /* Center/LFE */
5232 if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "Center Playback Switch",
5233 HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0)
5234 return err;
5235 if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "LFE Playback Switch",
5236 HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
5237 return err;
5238 } else {
5239 for (idx = 0; idx < ARRAY_SIZE(alc861_dac_nids) - 1; idx++)
5240 if (nid == alc861_dac_nids[idx])
5241 break;
5242 sprintf(name, "%s Playback Switch", chname[idx]);
5243 if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name,
5244 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
5245 return err;
5246 }
5247 }
5248 return 0;
5249}
5250
5251static int alc861_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin)
5252{
5253 int err;
5254 hda_nid_t nid;
5255
5256 if (! pin)
5257 return 0;
5258
5259 if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) {
5260 nid = 0x03;
5261 if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
5262 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
5263 return err;
5264 spec->multiout.hp_nid = nid;
5265 }
5266 return 0;
5267}
5268
5269/* create playback/capture controls for input pins */
5270static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
5271{
Kailang Yangdf694da2005-12-05 19:42:22 +01005272 struct hda_input_mux *imux = &spec->private_imux;
5273 int i, err, idx, idx1;
5274
5275 for (i = 0; i < AUTO_PIN_LAST; i++) {
5276 switch(cfg->input_pins[i]) {
5277 case 0x0c:
5278 idx1 = 1;
5279 idx = 2; // Line In
5280 break;
5281 case 0x0f:
5282 idx1 = 2;
5283 idx = 2; // Line In
5284 break;
5285 case 0x0d:
5286 idx1 = 0;
5287 idx = 1; // Mic In
5288 break;
5289 case 0x10:
5290 idx1 = 3;
5291 idx = 1; // Mic In
5292 break;
5293 case 0x11:
5294 idx1 = 4;
5295 idx = 0; // CD
5296 break;
5297 default:
5298 continue;
5299 }
5300
Takashi Iwai4a471b72005-12-07 13:56:29 +01005301 err = new_analog_input(spec, cfg->input_pins[i],
5302 auto_pin_cfg_labels[i], idx, 0x15);
Kailang Yangdf694da2005-12-05 19:42:22 +01005303 if (err < 0)
5304 return err;
5305
Takashi Iwai4a471b72005-12-07 13:56:29 +01005306 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Kailang Yangdf694da2005-12-05 19:42:22 +01005307 imux->items[imux->num_items].index = idx1;
5308 imux->num_items++;
5309 }
5310 return 0;
5311}
5312
5313static struct snd_kcontrol_new alc861_capture_mixer[] = {
5314 HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
5315 HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
5316
5317 {
5318 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5319 /* The multiple "Capture Source" controls confuse alsamixer
5320 * So call somewhat different..
5321 *FIXME: the controls appear in the "playback" view!
5322 */
5323 /* .name = "Capture Source", */
5324 .name = "Input Source",
5325 .count = 1,
5326 .info = alc_mux_enum_info,
5327 .get = alc_mux_enum_get,
5328 .put = alc_mux_enum_put,
5329 },
5330 { } /* end */
5331};
5332
5333static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid,
5334 int pin_type, int dac_idx)
5335{
5336 /* set as output */
5337
5338 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
5339 snd_hda_codec_write(codec, dac_idx, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
5340
5341}
5342
5343static void alc861_auto_init_multi_out(struct hda_codec *codec)
5344{
5345 struct alc_spec *spec = codec->spec;
5346 int i;
5347
5348 for (i = 0; i < spec->autocfg.line_outs; i++) {
5349 hda_nid_t nid = spec->autocfg.line_out_pins[i];
5350 if (nid)
5351 alc861_auto_set_output_and_unmute(codec, nid, PIN_OUT, spec->multiout.dac_nids[i]);
5352 }
5353}
5354
5355static void alc861_auto_init_hp_out(struct hda_codec *codec)
5356{
5357 struct alc_spec *spec = codec->spec;
5358 hda_nid_t pin;
5359
5360 pin = spec->autocfg.hp_pin;
5361 if (pin) /* connect to front */
5362 alc861_auto_set_output_and_unmute(codec, pin, PIN_HP, spec->multiout.dac_nids[0]);
5363}
5364
5365static void alc861_auto_init_analog_input(struct hda_codec *codec)
5366{
5367 struct alc_spec *spec = codec->spec;
5368 int i;
5369
5370 for (i = 0; i < AUTO_PIN_LAST; i++) {
5371 hda_nid_t nid = spec->autocfg.input_pins[i];
5372 if ((nid>=0x0c) && (nid <=0x11)) {
5373 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
5374 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
5375 }
5376 }
5377}
5378
5379/* parse the BIOS configuration and set up the alc_spec */
5380/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
5381static int alc861_parse_auto_config(struct hda_codec *codec)
5382{
5383 struct alc_spec *spec = codec->spec;
5384 int err;
5385 static hda_nid_t alc861_ignore[] = { 0x1d, 0 };
5386
5387 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
5388 alc861_ignore)) < 0)
5389 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01005390 if (! spec->autocfg.line_outs)
Kailang Yangdf694da2005-12-05 19:42:22 +01005391 return 0; /* can't find valid BIOS pin config */
5392
5393 if ((err = alc861_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 ||
5394 (err = alc861_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
5395 (err = alc861_auto_create_hp_ctls(spec, spec->autocfg.hp_pin)) < 0 ||
5396 (err = alc861_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
5397 return err;
5398
5399 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5400
5401 if (spec->autocfg.dig_out_pin)
5402 spec->multiout.dig_out_nid = ALC861_DIGOUT_NID;
5403
5404 if (spec->kctl_alloc)
5405 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
5406
5407 spec->init_verbs[spec->num_init_verbs++] = alc861_auto_init_verbs;
5408
5409 spec->input_mux = &spec->private_imux;
5410
5411 spec->adc_nids = alc861_adc_nids;
5412 spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
5413 spec->mixers[spec->num_mixers] = alc861_capture_mixer;
5414 spec->num_mixers++;
5415
5416 return 1;
5417}
5418
Takashi Iwaiae6b8132006-03-03 16:47:17 +01005419/* additional initialization for auto-configuration model */
5420static void alc861_auto_init(struct hda_codec *codec)
Kailang Yangdf694da2005-12-05 19:42:22 +01005421{
Kailang Yangdf694da2005-12-05 19:42:22 +01005422 alc861_auto_init_multi_out(codec);
5423 alc861_auto_init_hp_out(codec);
5424 alc861_auto_init_analog_input(codec);
Kailang Yangdf694da2005-12-05 19:42:22 +01005425}
5426
5427
5428/*
5429 * configuration and preset
5430 */
5431static struct hda_board_config alc861_cfg_tbl[] = {
5432 { .modelname = "3stack", .config = ALC861_3ST },
5433 { .pci_subvendor = 0x8086, .pci_subdevice = 0xd600, .config = ALC861_3ST },
5434 { .modelname = "3stack-dig", .config = ALC861_3ST_DIG },
5435 { .modelname = "6stack-dig", .config = ALC861_6ST_DIG },
5436 { .modelname = "auto", .config = ALC861_AUTO },
5437 {}
5438};
5439
5440static struct alc_config_preset alc861_presets[] = {
5441 [ALC861_3ST] = {
5442 .mixers = { alc861_3ST_mixer },
5443 .init_verbs = { alc861_threestack_init_verbs },
5444 .num_dacs = ARRAY_SIZE(alc861_dac_nids),
5445 .dac_nids = alc861_dac_nids,
5446 .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
5447 .channel_mode = alc861_threestack_modes,
5448 .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
5449 .adc_nids = alc861_adc_nids,
5450 .input_mux = &alc861_capture_source,
5451 },
5452 [ALC861_3ST_DIG] = {
5453 .mixers = { alc861_base_mixer },
5454 .init_verbs = { alc861_threestack_init_verbs },
5455 .num_dacs = ARRAY_SIZE(alc861_dac_nids),
5456 .dac_nids = alc861_dac_nids,
5457 .dig_out_nid = ALC861_DIGOUT_NID,
5458 .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
5459 .channel_mode = alc861_threestack_modes,
5460 .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
5461 .adc_nids = alc861_adc_nids,
5462 .input_mux = &alc861_capture_source,
5463 },
5464 [ALC861_6ST_DIG] = {
5465 .mixers = { alc861_base_mixer },
5466 .init_verbs = { alc861_base_init_verbs },
5467 .num_dacs = ARRAY_SIZE(alc861_dac_nids),
5468 .dac_nids = alc861_dac_nids,
5469 .dig_out_nid = ALC861_DIGOUT_NID,
5470 .num_channel_mode = ARRAY_SIZE(alc861_8ch_modes),
5471 .channel_mode = alc861_8ch_modes,
5472 .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
5473 .adc_nids = alc861_adc_nids,
5474 .input_mux = &alc861_capture_source,
5475 },
5476};
5477
5478
5479static int patch_alc861(struct hda_codec *codec)
5480{
5481 struct alc_spec *spec;
5482 int board_config;
5483 int err;
5484
5485 spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
5486 if (spec == NULL)
5487 return -ENOMEM;
5488
5489 codec->spec = spec;
5490
5491 board_config = snd_hda_check_board_config(codec, alc861_cfg_tbl);
5492 if (board_config < 0 || board_config >= ALC861_MODEL_LAST) {
5493 printk(KERN_INFO "hda_codec: Unknown model for ALC861, trying auto-probe from BIOS...\n");
5494 board_config = ALC861_AUTO;
5495 }
5496
5497 if (board_config == ALC861_AUTO) {
5498 /* automatic parse from the BIOS config */
5499 err = alc861_parse_auto_config(codec);
5500 if (err < 0) {
5501 alc_free(codec);
5502 return err;
5503 } else if (! err) {
5504 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
5505 board_config = ALC861_3ST_DIG;
5506 }
5507 }
5508
5509 if (board_config != ALC861_AUTO)
5510 setup_preset(spec, &alc861_presets[board_config]);
5511
5512 spec->stream_name_analog = "ALC861 Analog";
5513 spec->stream_analog_playback = &alc861_pcm_analog_playback;
5514 spec->stream_analog_capture = &alc861_pcm_analog_capture;
5515
5516 spec->stream_name_digital = "ALC861 Digital";
5517 spec->stream_digital_playback = &alc861_pcm_digital_playback;
5518 spec->stream_digital_capture = &alc861_pcm_digital_capture;
5519
5520 codec->patch_ops = alc_patch_ops;
5521 if (board_config == ALC861_AUTO)
Takashi Iwaiae6b8132006-03-03 16:47:17 +01005522 spec->init_hook = alc861_auto_init;
Kailang Yangdf694da2005-12-05 19:42:22 +01005523
5524 return 0;
5525}
5526
5527/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07005528 * patch entries
5529 */
5530struct hda_codec_preset snd_hda_preset_realtek[] = {
5531 { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
Kailang Yangdf694da2005-12-05 19:42:22 +01005532 { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07005533 { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
5534 { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
Kailang Yangdf694da2005-12-05 19:42:22 +01005535 { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
5536 { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
5537 { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07005538 {} /* terminator */
5539};