blob: 49140322e769ea01f876d4f7604a2ccfda895e61 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Takashi Iwai0ac85512007-06-20 15:46:13 +02002 * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
3 * AD1986A, AD1988
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
Takashi Iwai2bac6472007-05-18 18:21:41 +02005 * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 *
7 * This driver is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This driver is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/init.h>
23#include <linux/delay.h>
24#include <linux/slab.h>
25#include <linux/pci.h>
Ingo Molnar62932df2006-01-16 16:34:20 +010026#include <linux/mutex.h>
27
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <sound/core.h>
29#include "hda_codec.h"
30#include "hda_local.h"
31
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020032struct ad198x_spec {
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010033 struct snd_kcontrol_new *mixers[5];
Takashi Iwai985be542005-11-02 18:26:49 +010034 int num_mixers;
35
Takashi Iwaid32410b12005-11-24 16:06:23 +010036 const struct hda_verb *init_verbs[5]; /* initialization verbs
Takashi Iwai985be542005-11-02 18:26:49 +010037 * don't forget NULL termination!
38 */
39 unsigned int num_init_verbs;
40
41 /* playback */
42 struct hda_multi_out multiout; /* playback set-up
43 * max_channels, dacs must be set
44 * dig_out_nid and hp_nid are optional
45 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +010046 unsigned int cur_eapd;
Takashi Iwai2125cad2006-03-27 12:52:22 +020047 unsigned int need_dac_fix;
Takashi Iwai985be542005-11-02 18:26:49 +010048
49 /* capture */
50 unsigned int num_adc_nids;
51 hda_nid_t *adc_nids;
52 hda_nid_t dig_in_nid; /* digital-in NID; optional */
53
54 /* capture source */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020055 const struct hda_input_mux *input_mux;
Takashi Iwai2e5b9562005-11-21 16:36:15 +010056 hda_nid_t *capsrc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010057 unsigned int cur_mux[3];
58
59 /* channel model */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +010060 const struct hda_channel_mode *channel_mode;
Takashi Iwai985be542005-11-02 18:26:49 +010061 int num_channel_mode;
62
63 /* PCM information */
Takashi Iwai2bac6472007-05-18 18:21:41 +020064 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
Takashi Iwai985be542005-11-02 18:26:49 +010065
Ingo Molnar62932df2006-01-16 16:34:20 +010066 struct mutex amp_mutex; /* PCM volume/mute control mutex */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020067 unsigned int spdif_route;
Takashi Iwaid32410b12005-11-24 16:06:23 +010068
69 /* dynamic controls, init_verbs and input_mux */
70 struct auto_pin_cfg autocfg;
71 unsigned int num_kctl_alloc, num_kctl_used;
72 struct snd_kcontrol_new *kctl_alloc;
73 struct hda_input_mux private_imux;
Takashi Iwai41923e42007-10-22 17:20:10 +020074 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
Takashi Iwaicb53c622007-08-10 17:21:45 +020075
Takashi Iwai8ab78c72007-09-06 14:29:53 +020076 unsigned int jack_present :1;
77
Takashi Iwaicb53c622007-08-10 17:21:45 +020078#ifdef CONFIG_SND_HDA_POWER_SAVE
79 struct hda_loopback_check loopback;
80#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +010081 /* for virtual master */
82 hda_nid_t vmaster_nid;
83 u32 vmaster_tlv[4];
84 const char **slave_vols;
85 const char **slave_sws;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086};
87
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020088/*
89 * input MUX handling (common part)
90 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010091static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020092{
93 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
94 struct ad198x_spec *spec = codec->spec;
95
96 return snd_hda_input_mux_info(spec->input_mux, uinfo);
97}
98
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010099static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200100{
101 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
102 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100103 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200104
Takashi Iwai985be542005-11-02 18:26:49 +0100105 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200106 return 0;
107}
108
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100109static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200110{
111 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
112 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100113 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200114
115 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai2e5b9562005-11-21 16:36:15 +0100116 spec->capsrc_nids[adc_idx],
117 &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200118}
119
120/*
121 * initialization (common callbacks)
122 */
123static int ad198x_init(struct hda_codec *codec)
124{
125 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100126 int i;
127
128 for (i = 0; i < spec->num_init_verbs; i++)
129 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200130 return 0;
131}
132
Takashi Iwai2134ea42008-01-10 16:53:55 +0100133static const char *ad_slave_vols[] = {
134 "Front Playback Volume",
135 "Surround Playback Volume",
136 "Center Playback Volume",
137 "LFE Playback Volume",
138 "Side Playback Volume",
139 "Headphone Playback Volume",
140 "Mono Playback Volume",
Takashi Iwai628ed132008-01-25 11:56:57 +0100141 "Speaker Playback Volume",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100142 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100143 NULL
144};
145
146static const char *ad_slave_sws[] = {
147 "Front Playback Switch",
148 "Surround Playback Switch",
149 "Center Playback Switch",
150 "LFE Playback Switch",
151 "Side Playback Switch",
152 "Headphone Playback Switch",
153 "Mono Playback Switch",
Takashi Iwai628ed132008-01-25 11:56:57 +0100154 "Speaker Playback Switch",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100155 "IEC958 Playback Switch",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100156 NULL
157};
158
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200159static int ad198x_build_controls(struct hda_codec *codec)
160{
161 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100162 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200163 int err;
164
Takashi Iwai985be542005-11-02 18:26:49 +0100165 for (i = 0; i < spec->num_mixers; i++) {
166 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
167 if (err < 0)
168 return err;
169 }
170 if (spec->multiout.dig_out_nid) {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200171 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100172 if (err < 0)
173 return err;
174 }
175 if (spec->dig_in_nid) {
176 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
177 if (err < 0)
178 return err;
179 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100180
181 /* if we have no master control, let's create it */
182 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
183 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
184 HDA_OUTPUT, spec->vmaster_tlv);
185 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
186 spec->vmaster_tlv,
187 (spec->slave_vols ?
188 spec->slave_vols : ad_slave_vols));
189 if (err < 0)
190 return err;
191 }
192 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
193 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
194 NULL,
195 (spec->slave_sws ?
196 spec->slave_sws : ad_slave_sws));
197 if (err < 0)
198 return err;
199 }
200
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200201 return 0;
202}
203
Takashi Iwaicb53c622007-08-10 17:21:45 +0200204#ifdef CONFIG_SND_HDA_POWER_SAVE
205static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
206{
207 struct ad198x_spec *spec = codec->spec;
208 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
209}
210#endif
211
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200212/*
213 * Analog playback callbacks
214 */
215static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
216 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100217 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200218{
219 struct ad198x_spec *spec = codec->spec;
220 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
221}
222
223static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
224 struct hda_codec *codec,
225 unsigned int stream_tag,
226 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100227 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200228{
229 struct ad198x_spec *spec = codec->spec;
230 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
231 format, substream);
232}
233
234static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
235 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100236 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200237{
238 struct ad198x_spec *spec = codec->spec;
239 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
240}
241
242/*
243 * Digital out
244 */
245static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
246 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100247 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200248{
249 struct ad198x_spec *spec = codec->spec;
250 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
251}
252
253static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
254 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100255 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200256{
257 struct ad198x_spec *spec = codec->spec;
258 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
259}
260
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200261static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
262 struct hda_codec *codec,
263 unsigned int stream_tag,
264 unsigned int format,
265 struct snd_pcm_substream *substream)
266{
267 struct ad198x_spec *spec = codec->spec;
268 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
269 format, substream);
270}
271
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200272/*
273 * Analog capture
274 */
275static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
276 struct hda_codec *codec,
277 unsigned int stream_tag,
278 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100279 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200280{
281 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100282 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
283 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200284 return 0;
285}
286
287static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
288 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100289 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200290{
291 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100292 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
293 0, 0, 0);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200294 return 0;
295}
296
297
298/*
299 */
300static struct hda_pcm_stream ad198x_pcm_analog_playback = {
301 .substreams = 1,
302 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100303 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200304 .nid = 0, /* fill later */
305 .ops = {
306 .open = ad198x_playback_pcm_open,
307 .prepare = ad198x_playback_pcm_prepare,
308 .cleanup = ad198x_playback_pcm_cleanup
309 },
310};
311
312static struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100313 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200314 .channels_min = 2,
315 .channels_max = 2,
316 .nid = 0, /* fill later */
317 .ops = {
318 .prepare = ad198x_capture_pcm_prepare,
319 .cleanup = ad198x_capture_pcm_cleanup
320 },
321};
322
323static struct hda_pcm_stream ad198x_pcm_digital_playback = {
324 .substreams = 1,
325 .channels_min = 2,
326 .channels_max = 2,
327 .nid = 0, /* fill later */
328 .ops = {
329 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200330 .close = ad198x_dig_playback_pcm_close,
331 .prepare = ad198x_dig_playback_pcm_prepare
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200332 },
333};
334
Takashi Iwai985be542005-11-02 18:26:49 +0100335static struct hda_pcm_stream ad198x_pcm_digital_capture = {
336 .substreams = 1,
337 .channels_min = 2,
338 .channels_max = 2,
339 /* NID is set in alc_build_pcms */
340};
341
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200342static int ad198x_build_pcms(struct hda_codec *codec)
343{
344 struct ad198x_spec *spec = codec->spec;
345 struct hda_pcm *info = spec->pcm_rec;
346
347 codec->num_pcms = 1;
348 codec->pcm_info = info;
349
350 info->name = "AD198x Analog";
351 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
352 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
353 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
354 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100355 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
356 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200357
358 if (spec->multiout.dig_out_nid) {
359 info++;
360 codec->num_pcms++;
361 info->name = "AD198x Digital";
362 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
363 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100364 if (spec->dig_in_nid) {
365 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
366 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
367 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200368 }
369
370 return 0;
371}
372
373static void ad198x_free(struct hda_codec *codec)
374{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100375 struct ad198x_spec *spec = codec->spec;
376 unsigned int i;
377
378 if (spec->kctl_alloc) {
379 for (i = 0; i < spec->num_kctl_used; i++)
380 kfree(spec->kctl_alloc[i].name);
381 kfree(spec->kctl_alloc);
382 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200383 kfree(codec->spec);
384}
385
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200386static struct hda_codec_ops ad198x_patch_ops = {
387 .build_controls = ad198x_build_controls,
388 .build_pcms = ad198x_build_pcms,
389 .init = ad198x_init,
390 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200391#ifdef CONFIG_SND_HDA_POWER_SAVE
392 .check_power_status = ad198x_check_power_status,
393#endif
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200394};
395
396
397/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100398 * EAPD control
399 * the private value = nid | (invert << 8)
400 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200401#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100402
403static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
404 struct snd_ctl_elem_value *ucontrol)
405{
406 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
407 struct ad198x_spec *spec = codec->spec;
408 int invert = (kcontrol->private_value >> 8) & 1;
409 if (invert)
410 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
411 else
412 ucontrol->value.integer.value[0] = spec->cur_eapd;
413 return 0;
414}
415
416static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
417 struct snd_ctl_elem_value *ucontrol)
418{
419 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
420 struct ad198x_spec *spec = codec->spec;
421 int invert = (kcontrol->private_value >> 8) & 1;
422 hda_nid_t nid = kcontrol->private_value & 0xff;
423 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100424 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwai18a815d2006-03-01 19:54:39 +0100425 if (invert)
426 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200427 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100428 return 0;
429 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200430 snd_hda_codec_write_cache(codec, nid,
431 0, AC_VERB_SET_EAPD_BTLENABLE,
432 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100433 return 1;
434}
435
Takashi Iwai9230d212006-03-13 13:49:49 +0100436static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
437 struct snd_ctl_elem_info *uinfo);
438static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
439 struct snd_ctl_elem_value *ucontrol);
440static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
441 struct snd_ctl_elem_value *ucontrol);
442
443
Takashi Iwai18a815d2006-03-01 19:54:39 +0100444/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200445 * AD1986A specific
446 */
447
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448#define AD1986A_SPDIF_OUT 0x02
449#define AD1986A_FRONT_DAC 0x03
450#define AD1986A_SURR_DAC 0x04
451#define AD1986A_CLFE_DAC 0x05
452#define AD1986A_ADC 0x06
453
454static hda_nid_t ad1986a_dac_nids[3] = {
455 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
456};
Takashi Iwai985be542005-11-02 18:26:49 +0100457static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +0100458static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459
460static struct hda_input_mux ad1986a_capture_source = {
461 .num_items = 7,
462 .items = {
463 { "Mic", 0x0 },
464 { "CD", 0x1 },
465 { "Aux", 0x3 },
466 { "Line", 0x4 },
467 { "Mix", 0x5 },
468 { "Mono", 0x6 },
469 { "Phone", 0x7 },
470 },
471};
472
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473
Takashi Iwai532d5382007-07-27 19:02:40 +0200474static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
475 .ops = &snd_hda_bind_vol,
476 .values = {
477 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
478 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
479 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
480 0
481 },
482};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483
Takashi Iwai532d5382007-07-27 19:02:40 +0200484static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
485 .ops = &snd_hda_bind_sw,
486 .values = {
487 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
488 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
489 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
490 0
491 },
492};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
494/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 * mixers
496 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100497static struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200498 /*
499 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
500 */
501 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
502 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
504 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
505 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
506 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
507 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
508 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
509 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
510 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
511 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
512 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
513 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
514 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
515 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
516 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
517 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
518 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
519 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
520 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100521 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
523 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
524 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
525 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
526 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
527 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
528 {
529 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
530 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200531 .info = ad198x_mux_enum_info,
532 .get = ad198x_mux_enum_get,
533 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 },
535 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
536 { } /* end */
537};
538
Takashi Iwai9230d212006-03-13 13:49:49 +0100539/* additional mixers for 3stack mode */
540static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
541 {
542 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
543 .name = "Channel Mode",
544 .info = ad198x_ch_mode_info,
545 .get = ad198x_ch_mode_get,
546 .put = ad198x_ch_mode_put,
547 },
548 { } /* end */
549};
550
551/* laptop model - 2ch only */
552static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
553
Takashi Iwai20a45e82007-08-15 22:20:45 +0200554/* master controls both pins 0x1a and 0x1b */
555static struct hda_bind_ctls ad1986a_laptop_master_vol = {
556 .ops = &snd_hda_bind_vol,
557 .values = {
558 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
559 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
560 0,
561 },
562};
563
564static struct hda_bind_ctls ad1986a_laptop_master_sw = {
565 .ops = &snd_hda_bind_sw,
566 .values = {
567 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
568 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
569 0,
570 },
571};
572
Takashi Iwai9230d212006-03-13 13:49:49 +0100573static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
574 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
575 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200576 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
577 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100578 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
579 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
580 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
581 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
582 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
583 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
584 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
585 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100586 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai9230d212006-03-13 13:49:49 +0100587 /* HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
588 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
589 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
590 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
591 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
592 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
593 {
594 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
595 .name = "Capture Source",
596 .info = ad198x_mux_enum_info,
597 .get = ad198x_mux_enum_get,
598 .put = ad198x_mux_enum_put,
599 },
600 { } /* end */
601};
602
Takashi Iwai825aa972006-03-17 10:50:49 +0100603/* laptop-eapd model - 2ch only */
604
Takashi Iwai825aa972006-03-17 10:50:49 +0100605static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
606 .num_items = 3,
607 .items = {
608 { "Mic", 0x0 },
609 { "Internal Mic", 0x4 },
610 { "Mix", 0x5 },
611 },
612};
613
614static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200615 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
616 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai825aa972006-03-17 10:50:49 +0100617 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
618 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
619 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT),
620 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT),
621 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
622 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100623 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai825aa972006-03-17 10:50:49 +0100624 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
625 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
626 {
627 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
628 .name = "Capture Source",
629 .info = ad198x_mux_enum_info,
630 .get = ad198x_mux_enum_get,
631 .put = ad198x_mux_enum_put,
632 },
633 {
634 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
635 .name = "External Amplifier",
636 .info = ad198x_eapd_info,
637 .get = ad198x_eapd_get,
638 .put = ad198x_eapd_put,
639 .private_value = 0x1b | (1 << 8), /* port-D, inversed */
640 },
641 { } /* end */
642};
643
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200644/* laptop-automute - 2ch only */
645
646static void ad1986a_update_hp(struct hda_codec *codec)
647{
648 struct ad198x_spec *spec = codec->spec;
649 unsigned int mute;
650
651 if (spec->jack_present)
652 mute = HDA_AMP_MUTE; /* mute internal speaker */
653 else
654 /* unmute internal speaker if necessary */
655 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
656 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
657 HDA_AMP_MUTE, mute);
658}
659
660static void ad1986a_hp_automute(struct hda_codec *codec)
661{
662 struct ad198x_spec *spec = codec->spec;
663 unsigned int present;
664
665 present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0);
Takashi Iwai53eb1b82007-10-17 10:09:32 +0200666 /* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
667 spec->jack_present = !(present & 0x80000000);
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200668 ad1986a_update_hp(codec);
669}
670
671#define AD1986A_HP_EVENT 0x37
672
673static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
674{
675 if ((res >> 26) != AD1986A_HP_EVENT)
676 return;
677 ad1986a_hp_automute(codec);
678}
679
680static int ad1986a_hp_init(struct hda_codec *codec)
681{
682 ad198x_init(codec);
683 ad1986a_hp_automute(codec);
684 return 0;
685}
686
687/* bind hp and internal speaker mute (with plug check) */
688static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
689 struct snd_ctl_elem_value *ucontrol)
690{
691 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
692 long *valp = ucontrol->value.integer.value;
693 int change;
694
695 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
696 HDA_AMP_MUTE,
697 valp[0] ? 0 : HDA_AMP_MUTE);
698 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
699 HDA_AMP_MUTE,
700 valp[1] ? 0 : HDA_AMP_MUTE);
701 if (change)
702 ad1986a_update_hp(codec);
703 return change;
704}
705
706static struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = {
707 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
708 {
709 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
710 .name = "Master Playback Switch",
711 .info = snd_hda_mixer_amp_switch_info,
712 .get = snd_hda_mixer_amp_switch_get,
713 .put = ad1986a_hp_master_sw_put,
714 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
715 },
716 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
717 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
718 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT),
719 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT),
720 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
721 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
722 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
723 HDA_CODEC_VOLUME("Beep Playback Volume", 0x18, 0x0, HDA_OUTPUT),
724 HDA_CODEC_MUTE("Beep Playback Switch", 0x18, 0x0, HDA_OUTPUT),
725 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
726 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
727 {
728 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
729 .name = "Capture Source",
730 .info = ad198x_mux_enum_info,
731 .get = ad198x_mux_enum_get,
732 .put = ad198x_mux_enum_put,
733 },
734 {
735 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
736 .name = "External Amplifier",
737 .info = ad198x_eapd_info,
738 .get = ad198x_eapd_get,
739 .put = ad198x_eapd_put,
740 .private_value = 0x1b | (1 << 8), /* port-D, inversed */
741 },
742 { } /* end */
743};
744
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745/*
746 * initialization verbs
747 */
748static struct hda_verb ad1986a_init_verbs[] = {
749 /* Front, Surround, CLFE DAC; mute as default */
750 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
751 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
752 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
753 /* Downmix - off */
754 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
755 /* HP, Line-Out, Surround, CLFE selectors */
756 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
757 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
758 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
759 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
760 /* Mono selector */
761 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
762 /* Mic selector: Mic 1/2 pin */
763 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
764 /* Line-in selector: Line-in */
765 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
766 /* Mic 1/2 swap */
767 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
768 /* Record selector: mic */
769 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
770 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
771 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
772 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
773 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
774 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
775 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
776 /* PC beep */
777 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
778 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
779 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
780 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
781 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
782 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
783 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200784 /* HP Pin */
785 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
786 /* Front, Surround, CLFE Pins */
787 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
788 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
789 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
790 /* Mono Pin */
791 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
792 /* Mic Pin */
793 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
794 /* Line, Aux, CD, Beep-In Pin */
795 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
796 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
797 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
798 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
799 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 { } /* end */
801};
802
Takashi Iwai9230d212006-03-13 13:49:49 +0100803static struct hda_verb ad1986a_ch2_init[] = {
804 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200805 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
806 /* Line-in selectors */
807 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100808 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200809 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
810 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
811 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100812 { } /* end */
813};
814
815static struct hda_verb ad1986a_ch4_init[] = {
816 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200817 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
818 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100819 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200820 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
821 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100822 { } /* end */
823};
824
825static struct hda_verb ad1986a_ch6_init[] = {
826 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200827 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
828 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100829 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200830 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
831 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100832 { } /* end */
833};
834
835static struct hda_channel_mode ad1986a_modes[3] = {
836 { 2, ad1986a_ch2_init },
837 { 4, ad1986a_ch4_init },
838 { 6, ad1986a_ch6_init },
839};
840
Takashi Iwai825aa972006-03-17 10:50:49 +0100841/* eapd initialization */
842static struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +0100843 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa972006-03-17 10:50:49 +0100844 {}
845};
846
Tobin Davisf36090f2007-01-08 11:07:12 +0100847/* Ultra initialization */
848static struct hda_verb ad1986a_ultra_init[] = {
849 /* eapd initialization */
850 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
851 /* CLFE -> Mic in */
852 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
853 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
854 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
855 { } /* end */
856};
857
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200858/* pin sensing on HP jack */
859static struct hda_verb ad1986a_hp_init_verbs[] = {
860 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
861 {}
862};
863
864
Takashi Iwai9230d212006-03-13 13:49:49 +0100865/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100866enum {
867 AD1986A_6STACK,
868 AD1986A_3STACK,
869 AD1986A_LAPTOP,
870 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200871 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +0100872 AD1986A_ULTRA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100873 AD1986A_MODELS
874};
Takashi Iwai9230d212006-03-13 13:49:49 +0100875
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100876static const char *ad1986a_models[AD1986A_MODELS] = {
877 [AD1986A_6STACK] = "6stack",
878 [AD1986A_3STACK] = "3stack",
879 [AD1986A_LAPTOP] = "laptop",
880 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200881 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +0100882 [AD1986A_ULTRA] = "ultra",
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100883};
884
885static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
886 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100887 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100888 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +0100889 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100890 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
891 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
892 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
893 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +0100894 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +0200895 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100896 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
897 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
898 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
899 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
900 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +0100901 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Takashi Iwai7db756f2007-12-24 14:36:09 +0100902 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
Tobin Davis18768992007-03-12 22:20:51 +0100903 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100904 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
905 SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),
906 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),
907 SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),
Tobin Davisf36090f2007-01-08 11:07:12 +0100908 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaiac3e3742007-12-17 17:14:18 +0100909 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +0100910 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100911 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200912 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100913 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +0100914 {}
915};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916
Takashi Iwaicb53c622007-08-10 17:21:45 +0200917#ifdef CONFIG_SND_HDA_POWER_SAVE
918static struct hda_amp_list ad1986a_loopbacks[] = {
919 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
920 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
921 { 0x15, HDA_OUTPUT, 0 }, /* CD */
922 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
923 { 0x17, HDA_OUTPUT, 0 }, /* Line */
924 { } /* end */
925};
926#endif
927
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928static int patch_ad1986a(struct hda_codec *codec)
929{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200930 struct ad198x_spec *spec;
Takashi Iwai9230d212006-03-13 13:49:49 +0100931 int board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932
Takashi Iwaie560d8d2005-09-09 14:21:46 +0200933 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 if (spec == NULL)
935 return -ENOMEM;
936
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 codec->spec = spec;
938
939 spec->multiout.max_channels = 6;
940 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
941 spec->multiout.dac_nids = ad1986a_dac_nids;
942 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +0100943 spec->num_adc_nids = 1;
944 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +0100945 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200946 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +0100947 spec->num_mixers = 1;
948 spec->mixers[0] = ad1986a_mixers;
949 spec->num_init_verbs = 1;
950 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +0200951#ifdef CONFIG_SND_HDA_POWER_SAVE
952 spec->loopback.amplist = ad1986a_loopbacks;
953#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +0100954 spec->vmaster_nid = 0x1b;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200956 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957
Takashi Iwai9230d212006-03-13 13:49:49 +0100958 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100959 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
960 ad1986a_models,
961 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +0100962 switch (board_config) {
963 case AD1986A_3STACK:
964 spec->num_mixers = 2;
965 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +0200966 spec->num_init_verbs = 2;
967 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +0100968 spec->channel_mode = ad1986a_modes;
969 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +0200970 spec->need_dac_fix = 1;
971 spec->multiout.max_channels = 2;
972 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +0100973 break;
974 case AD1986A_LAPTOP:
975 spec->mixers[0] = ad1986a_laptop_mixers;
976 spec->multiout.max_channels = 2;
977 spec->multiout.num_dacs = 1;
978 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
979 break;
Takashi Iwai825aa972006-03-17 10:50:49 +0100980 case AD1986A_LAPTOP_EAPD:
981 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
982 spec->num_init_verbs = 2;
983 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
984 spec->multiout.max_channels = 2;
985 spec->multiout.num_dacs = 1;
986 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
987 spec->multiout.dig_out_nid = 0;
988 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
989 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200990 case AD1986A_LAPTOP_AUTOMUTE:
991 spec->mixers[0] = ad1986a_laptop_automute_mixers;
992 spec->num_init_verbs = 3;
993 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
994 spec->init_verbs[2] = ad1986a_hp_init_verbs;
995 spec->multiout.max_channels = 2;
996 spec->multiout.num_dacs = 1;
997 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
998 spec->multiout.dig_out_nid = 0;
999 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1000 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1001 codec->patch_ops.init = ad1986a_hp_init;
1002 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001003 case AD1986A_ULTRA:
1004 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1005 spec->num_init_verbs = 2;
1006 spec->init_verbs[1] = ad1986a_ultra_init;
1007 spec->multiout.max_channels = 2;
1008 spec->multiout.num_dacs = 1;
1009 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1010 spec->multiout.dig_out_nid = 0;
1011 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001012 }
1013
Takashi Iwaid29240c2007-10-26 12:35:56 +02001014 /* AD1986A has a hardware problem that it can't share a stream
1015 * with multiple output pins. The copy of front to surrounds
1016 * causes noisy or silent outputs at a certain timing, e.g.
1017 * changing the volume.
1018 * So, let's disable the shared stream.
1019 */
1020 spec->multiout.no_share_stream = 1;
1021
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 return 0;
1023}
1024
1025/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001026 * AD1983 specific
1027 */
1028
1029#define AD1983_SPDIF_OUT 0x02
1030#define AD1983_DAC 0x03
1031#define AD1983_ADC 0x04
1032
1033static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001034static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001035static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001036
1037static struct hda_input_mux ad1983_capture_source = {
1038 .num_items = 4,
1039 .items = {
1040 { "Mic", 0x0 },
1041 { "Line", 0x1 },
1042 { "Mix", 0x2 },
1043 { "Mix Mono", 0x3 },
1044 },
1045};
1046
1047/*
1048 * SPDIF playback route
1049 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001050static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001051{
1052 static char *texts[] = { "PCM", "ADC" };
1053
1054 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1055 uinfo->count = 1;
1056 uinfo->value.enumerated.items = 2;
1057 if (uinfo->value.enumerated.item > 1)
1058 uinfo->value.enumerated.item = 1;
1059 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1060 return 0;
1061}
1062
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001063static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001064{
1065 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1066 struct ad198x_spec *spec = codec->spec;
1067
1068 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1069 return 0;
1070}
1071
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001072static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001073{
1074 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1075 struct ad198x_spec *spec = codec->spec;
1076
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001077 if (ucontrol->value.enumerated.item[0] > 1)
1078 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001079 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1080 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001081 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1082 AC_VERB_SET_CONNECT_SEL,
1083 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001084 return 1;
1085 }
1086 return 0;
1087}
1088
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001089static struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001090 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1091 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1092 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1093 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1094 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1095 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1096 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1097 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1098 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1099 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1100 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1101 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1102 HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x10, 1, 0x0, HDA_OUTPUT),
1103 HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x10, 1, 0x0, HDA_OUTPUT),
1104 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
1105 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1106 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1107 {
1108 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1109 .name = "Capture Source",
1110 .info = ad198x_mux_enum_info,
1111 .get = ad198x_mux_enum_get,
1112 .put = ad198x_mux_enum_put,
1113 },
1114 {
1115 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001116 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001117 .info = ad1983_spdif_route_info,
1118 .get = ad1983_spdif_route_get,
1119 .put = ad1983_spdif_route_put,
1120 },
1121 { } /* end */
1122};
1123
1124static struct hda_verb ad1983_init_verbs[] = {
1125 /* Front, HP, Mono; mute as default */
1126 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1127 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1128 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1129 /* Beep, PCM, Mic, Line-In: mute */
1130 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1131 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1132 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1133 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1134 /* Front, HP selectors; from Mix */
1135 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1136 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1137 /* Mono selector; from Mix */
1138 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1139 /* Mic selector; Mic */
1140 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1141 /* Line-in selector: Line-in */
1142 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1143 /* Mic boost: 0dB */
1144 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1145 /* Record selector: mic */
1146 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1147 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1148 /* SPDIF route: PCM */
1149 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1150 /* Front Pin */
1151 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1152 /* HP Pin */
1153 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1154 /* Mono Pin */
1155 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1156 /* Mic Pin */
1157 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1158 /* Line Pin */
1159 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1160 { } /* end */
1161};
1162
Takashi Iwaicb53c622007-08-10 17:21:45 +02001163#ifdef CONFIG_SND_HDA_POWER_SAVE
1164static struct hda_amp_list ad1983_loopbacks[] = {
1165 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1166 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1167 { } /* end */
1168};
1169#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001170
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001171static int patch_ad1983(struct hda_codec *codec)
1172{
1173 struct ad198x_spec *spec;
1174
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001175 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001176 if (spec == NULL)
1177 return -ENOMEM;
1178
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001179 codec->spec = spec;
1180
1181 spec->multiout.max_channels = 2;
1182 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1183 spec->multiout.dac_nids = ad1983_dac_nids;
1184 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001185 spec->num_adc_nids = 1;
1186 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001187 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001188 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001189 spec->num_mixers = 1;
1190 spec->mixers[0] = ad1983_mixers;
1191 spec->num_init_verbs = 1;
1192 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001193 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001194#ifdef CONFIG_SND_HDA_POWER_SAVE
1195 spec->loopback.amplist = ad1983_loopbacks;
1196#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001197 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001198
1199 codec->patch_ops = ad198x_patch_ops;
1200
1201 return 0;
1202}
1203
1204
1205/*
1206 * AD1981 HD specific
1207 */
1208
1209#define AD1981_SPDIF_OUT 0x02
1210#define AD1981_DAC 0x03
1211#define AD1981_ADC 0x04
1212
1213static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001214static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001215static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001216
1217/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
1218static struct hda_input_mux ad1981_capture_source = {
1219 .num_items = 7,
1220 .items = {
1221 { "Front Mic", 0x0 },
1222 { "Line", 0x1 },
1223 { "Mix", 0x2 },
1224 { "Mix Mono", 0x3 },
1225 { "CD", 0x4 },
1226 { "Mic", 0x6 },
1227 { "Aux", 0x7 },
1228 },
1229};
1230
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001231static struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001232 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1233 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1234 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1235 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1236 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1237 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1238 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1239 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1240 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1241 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1242 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1243 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1244 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1245 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1246 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1247 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1248 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1249 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1250 HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
1251 HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x0d, 1, 0x0, HDA_OUTPUT),
1252 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
1253 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
1254 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1255 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1256 {
1257 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1258 .name = "Capture Source",
1259 .info = ad198x_mux_enum_info,
1260 .get = ad198x_mux_enum_get,
1261 .put = ad198x_mux_enum_put,
1262 },
1263 /* identical with AD1983 */
1264 {
1265 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001266 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001267 .info = ad1983_spdif_route_info,
1268 .get = ad1983_spdif_route_get,
1269 .put = ad1983_spdif_route_put,
1270 },
1271 { } /* end */
1272};
1273
1274static struct hda_verb ad1981_init_verbs[] = {
1275 /* Front, HP, Mono; mute as default */
1276 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1277 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1278 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1279 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1280 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1281 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1282 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1283 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1284 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1285 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1286 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1287 /* Front, HP selectors; from Mix */
1288 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1289 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1290 /* Mono selector; from Mix */
1291 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1292 /* Mic Mixer; select Front Mic */
1293 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1294 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1295 /* Mic boost: 0dB */
1296 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1297 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1298 /* Record selector: Front mic */
1299 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1300 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1301 /* SPDIF route: PCM */
1302 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1303 /* Front Pin */
1304 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1305 /* HP Pin */
1306 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1307 /* Mono Pin */
1308 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1309 /* Front & Rear Mic Pins */
1310 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1311 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1312 /* Line Pin */
1313 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1314 /* Digital Beep */
1315 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1316 /* Line-Out as Input: disabled */
1317 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1318 { } /* end */
1319};
1320
Takashi Iwaicb53c622007-08-10 17:21:45 +02001321#ifdef CONFIG_SND_HDA_POWER_SAVE
1322static struct hda_amp_list ad1981_loopbacks[] = {
1323 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1324 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1325 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1326 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1327 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1328 { } /* end */
1329};
1330#endif
1331
Takashi Iwai18a815d2006-03-01 19:54:39 +01001332/*
1333 * Patch for HP nx6320
1334 *
Tobin Davis18768992007-03-12 22:20:51 +01001335 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001336 * speaker output enabled _and_ mute-LED off.
1337 */
1338
1339#define AD1981_HP_EVENT 0x37
1340#define AD1981_MIC_EVENT 0x38
1341
1342static struct hda_verb ad1981_hp_init_verbs[] = {
1343 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1344 /* pin sensing on HP and Mic jacks */
1345 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1346 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1347 {}
1348};
1349
1350/* turn on/off EAPD (+ mute HP) as a master switch */
1351static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1352 struct snd_ctl_elem_value *ucontrol)
1353{
1354 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1355 struct ad198x_spec *spec = codec->spec;
1356
1357 if (! ad198x_eapd_put(kcontrol, ucontrol))
1358 return 0;
1359
1360 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001361 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1362 HDA_AMP_MUTE,
1363 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001364 return 1;
1365}
1366
1367/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02001368static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
1369 .ops = &snd_hda_bind_vol,
1370 .values = {
1371 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1372 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1373 0
1374 },
1375};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001376
1377/* mute internal speaker if HP is plugged */
1378static void ad1981_hp_automute(struct hda_codec *codec)
1379{
1380 unsigned int present;
1381
1382 present = snd_hda_codec_read(codec, 0x06, 0,
1383 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
Takashi Iwai47fd8302007-08-10 17:11:07 +02001384 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1385 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001386}
1387
1388/* toggle input of built-in and mic jack appropriately */
1389static void ad1981_hp_automic(struct hda_codec *codec)
1390{
1391 static struct hda_verb mic_jack_on[] = {
1392 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1393 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1394 {}
1395 };
1396 static struct hda_verb mic_jack_off[] = {
1397 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1398 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1399 {}
1400 };
1401 unsigned int present;
1402
1403 present = snd_hda_codec_read(codec, 0x08, 0,
1404 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1405 if (present)
1406 snd_hda_sequence_write(codec, mic_jack_on);
1407 else
1408 snd_hda_sequence_write(codec, mic_jack_off);
1409}
1410
1411/* unsolicited event for HP jack sensing */
1412static void ad1981_hp_unsol_event(struct hda_codec *codec,
1413 unsigned int res)
1414{
1415 res >>= 26;
1416 switch (res) {
1417 case AD1981_HP_EVENT:
1418 ad1981_hp_automute(codec);
1419 break;
1420 case AD1981_MIC_EVENT:
1421 ad1981_hp_automic(codec);
1422 break;
1423 }
1424}
1425
1426static struct hda_input_mux ad1981_hp_capture_source = {
1427 .num_items = 3,
1428 .items = {
1429 { "Mic", 0x0 },
1430 { "Docking-Station", 0x1 },
1431 { "Mix", 0x2 },
1432 },
1433};
1434
1435static struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001436 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001437 {
1438 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1439 .name = "Master Playback Switch",
1440 .info = ad198x_eapd_info,
1441 .get = ad198x_eapd_get,
1442 .put = ad1981_hp_master_sw_put,
1443 .private_value = 0x05,
1444 },
1445 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1446 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1447#if 0
1448 /* FIXME: analog mic/line loopback doesn't work with my tests...
1449 * (although recording is OK)
1450 */
1451 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1452 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1453 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1454 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1455 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1456 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1457 /* FIXME: does this laptop have analog CD connection? */
1458 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1459 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1460#endif
1461 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1462 HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
1463 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1464 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1465 {
1466 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1467 .name = "Capture Source",
1468 .info = ad198x_mux_enum_info,
1469 .get = ad198x_mux_enum_get,
1470 .put = ad198x_mux_enum_put,
1471 },
1472 { } /* end */
1473};
1474
1475/* initialize jack-sensing, too */
1476static int ad1981_hp_init(struct hda_codec *codec)
1477{
1478 ad198x_init(codec);
1479 ad1981_hp_automute(codec);
1480 ad1981_hp_automic(codec);
1481 return 0;
1482}
1483
Tobin Davis18768992007-03-12 22:20:51 +01001484/* configuration for Toshiba Laptops */
1485static struct hda_verb ad1981_toshiba_init_verbs[] = {
1486 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1487 /* pin sensing on HP and Mic jacks */
1488 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1489 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1490 {}
1491};
1492
1493static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
1494 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1495 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1496 { }
1497};
1498
Takashi Iwai01686c52006-04-18 12:54:11 +02001499/* configuration for Lenovo Thinkpad T60 */
1500static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
1501 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1502 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1503 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1504 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1505 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1506 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1507 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1508 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1509 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1510 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1511 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1512 {
1513 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1514 .name = "Capture Source",
1515 .info = ad198x_mux_enum_info,
1516 .get = ad198x_mux_enum_get,
1517 .put = ad198x_mux_enum_put,
1518 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001519 /* identical with AD1983 */
1520 {
1521 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1522 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1523 .info = ad1983_spdif_route_info,
1524 .get = ad1983_spdif_route_get,
1525 .put = ad1983_spdif_route_put,
1526 },
Takashi Iwai01686c52006-04-18 12:54:11 +02001527 { } /* end */
1528};
1529
1530static struct hda_input_mux ad1981_thinkpad_capture_source = {
1531 .num_items = 3,
1532 .items = {
1533 { "Mic", 0x0 },
1534 { "Mix", 0x2 },
1535 { "CD", 0x4 },
1536 },
1537};
1538
Takashi Iwai18a815d2006-03-01 19:54:39 +01001539/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001540enum {
1541 AD1981_BASIC,
1542 AD1981_HP,
1543 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001544 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001545 AD1981_MODELS
1546};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001547
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001548static const char *ad1981_models[AD1981_MODELS] = {
1549 [AD1981_HP] = "hp",
1550 [AD1981_THINKPAD] = "thinkpad",
1551 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001552 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001553};
1554
1555static struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001556 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001557 /* All HP models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001558 SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001559 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c52006-04-18 12:54:11 +02001560 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001561 SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001562 /* HP nx6320 (reversed SSID, H/W bug) */
1563 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001564 {}
1565};
1566
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001567static int patch_ad1981(struct hda_codec *codec)
1568{
1569 struct ad198x_spec *spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001570 int board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001571
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001572 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001573 if (spec == NULL)
1574 return -ENOMEM;
1575
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001576 codec->spec = spec;
1577
1578 spec->multiout.max_channels = 2;
1579 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1580 spec->multiout.dac_nids = ad1981_dac_nids;
1581 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001582 spec->num_adc_nids = 1;
1583 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001584 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001585 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001586 spec->num_mixers = 1;
1587 spec->mixers[0] = ad1981_mixers;
1588 spec->num_init_verbs = 1;
1589 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001590 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001591#ifdef CONFIG_SND_HDA_POWER_SAVE
1592 spec->loopback.amplist = ad1981_loopbacks;
1593#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001594 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001595
1596 codec->patch_ops = ad198x_patch_ops;
1597
Takashi Iwai18a815d2006-03-01 19:54:39 +01001598 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001599 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1600 ad1981_models,
1601 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001602 switch (board_config) {
1603 case AD1981_HP:
1604 spec->mixers[0] = ad1981_hp_mixers;
1605 spec->num_init_verbs = 2;
1606 spec->init_verbs[1] = ad1981_hp_init_verbs;
1607 spec->multiout.dig_out_nid = 0;
1608 spec->input_mux = &ad1981_hp_capture_source;
1609
1610 codec->patch_ops.init = ad1981_hp_init;
1611 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1612 break;
Takashi Iwai01686c52006-04-18 12:54:11 +02001613 case AD1981_THINKPAD:
1614 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c52006-04-18 12:54:11 +02001615 spec->input_mux = &ad1981_thinkpad_capture_source;
1616 break;
Tobin Davis18768992007-03-12 22:20:51 +01001617 case AD1981_TOSHIBA:
1618 spec->mixers[0] = ad1981_hp_mixers;
1619 spec->mixers[1] = ad1981_toshiba_mixers;
1620 spec->num_init_verbs = 2;
1621 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1622 spec->multiout.dig_out_nid = 0;
1623 spec->input_mux = &ad1981_hp_capture_source;
1624 codec->patch_ops.init = ad1981_hp_init;
1625 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1626 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001627 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001628 return 0;
1629}
1630
1631
1632/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001633 * AD1988
1634 *
1635 * Output pins and routes
1636 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001637 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001638 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1639 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1640 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1641 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1642 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1643 * port-F 0x16 (mute) <- 0x2a <- 06
1644 * port-G 0x24 (mute) <- 0x27 <- 05
1645 * port-H 0x25 (mute) <- 0x28 <- 0a
1646 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1647 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001648 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1649 * (*) DAC2/3/4 are swapped to DAC3/4/2 on AD198A rev.2 due to a h/w bug.
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001650 *
1651 * Input pins and routes
1652 *
1653 * pin boost mix input # / adc input #
1654 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1655 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1656 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1657 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1658 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1659 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1660 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1661 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1662 *
1663 *
1664 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001665 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001666 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001667 *
1668 * Inputs of Analog Mix (0x20)
1669 * 0:Port-B (front mic)
1670 * 1:Port-C/G/H (line-in)
1671 * 2:Port-A
1672 * 3:Port-D (line-in/2)
1673 * 4:Port-E/G/H (mic-in)
1674 * 5:Port-F (mic2-in)
1675 * 6:CD
1676 * 7:Beep
1677 *
1678 * ADC selection
1679 * 0:Port-A
1680 * 1:Port-B (front mic-in)
1681 * 2:Port-C (line-in)
1682 * 3:Port-F (mic2-in)
1683 * 4:Port-E (mic-in)
1684 * 5:CD
1685 * 6:Port-G
1686 * 7:Port-H
1687 * 8:Port-D (line-in/2)
1688 * 9:Mix
1689 *
1690 * Proposed pin assignments by the datasheet
1691 *
1692 * 6-stack
1693 * Port-A front headphone
1694 * B front mic-in
1695 * C rear line-in
1696 * D rear front-out
1697 * E rear mic-in
1698 * F rear surround
1699 * G rear CLFE
1700 * H rear side
1701 *
1702 * 3-stack
1703 * Port-A front headphone
1704 * B front mic
1705 * C rear line-in/surround
1706 * D rear front-out
1707 * E rear mic-in/CLFE
1708 *
1709 * laptop
1710 * Port-A headphone
1711 * B mic-in
1712 * C docking station
1713 * D internal speaker (with EAPD)
1714 * E/F quad mic array
1715 */
1716
1717
1718/* models */
1719enum {
1720 AD1988_6STACK,
1721 AD1988_6STACK_DIG,
1722 AD1988_3STACK,
1723 AD1988_3STACK_DIG,
1724 AD1988_LAPTOP,
1725 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01001726 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001727 AD1988_MODEL_LAST,
1728};
1729
Takashi Iwaid32410b12005-11-24 16:06:23 +01001730/* reivision id to check workarounds */
1731#define AD1988A_REV2 0x100200
1732
Takashi Iwai1a806f42006-07-03 15:58:16 +02001733#define is_rev2(codec) \
1734 ((codec)->vendor_id == 0x11d41988 && \
1735 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001736
1737/*
1738 * mixers
1739 */
1740
Takashi Iwaid32410b12005-11-24 16:06:23 +01001741static hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001742 0x04, 0x06, 0x05, 0x0a
1743};
1744
Takashi Iwaid32410b12005-11-24 16:06:23 +01001745static hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001746 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01001747};
1748
1749/* for AD1988A revision-2, DAC2-4 are swapped */
1750static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
1751 0x04, 0x05, 0x0a, 0x06
1752};
1753
1754static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001755 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01001756};
1757
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001758static hda_nid_t ad1988_adc_nids[3] = {
1759 0x08, 0x09, 0x0f
1760};
1761
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001762static hda_nid_t ad1988_capsrc_nids[3] = {
1763 0x0c, 0x0d, 0x0e
1764};
1765
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001766#define AD1988_SPDIF_OUT 0x02
1767#define AD1988_SPDIF_IN 0x07
1768
1769static struct hda_input_mux ad1988_6stack_capture_source = {
1770 .num_items = 5,
1771 .items = {
1772 { "Front Mic", 0x0 },
1773 { "Line", 0x1 },
1774 { "Mic", 0x4 },
1775 { "CD", 0x5 },
1776 { "Mix", 0x9 },
1777 },
1778};
1779
1780static struct hda_input_mux ad1988_laptop_capture_source = {
1781 .num_items = 3,
1782 .items = {
1783 { "Mic/Line", 0x0 },
1784 { "CD", 0x5 },
1785 { "Mix", 0x9 },
1786 },
1787};
1788
1789/*
1790 */
1791static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
1792 struct snd_ctl_elem_info *uinfo)
1793{
1794 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1795 struct ad198x_spec *spec = codec->spec;
1796 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
1797 spec->num_channel_mode);
1798}
1799
1800static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
1801 struct snd_ctl_elem_value *ucontrol)
1802{
1803 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1804 struct ad198x_spec *spec = codec->spec;
1805 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
1806 spec->num_channel_mode, spec->multiout.max_channels);
1807}
1808
1809static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
1810 struct snd_ctl_elem_value *ucontrol)
1811{
1812 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1813 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001814 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
1815 spec->num_channel_mode,
1816 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02001817 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02001818 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001819 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001820}
1821
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001822/* 6-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001823static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001824 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1825 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1826 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
1827 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
1828 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001829 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001830};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001831
Takashi Iwaid32410b12005-11-24 16:06:23 +01001832static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
1833 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1834 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1835 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
1836 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
1837 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001838 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001839};
1840
1841static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001842 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
1843 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
1844 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
1845 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
1846 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
1847 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
1848 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
1849
1850 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
1851 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
1852 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
1853 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
1854 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
1855 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
1856 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
1857 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
1858
1859 HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
1860 HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
1861
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001862 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001863 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
1864
1865 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
1866 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
1867
1868 { } /* end */
1869};
1870
1871/* 3-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001872static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001873 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01001874 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001875 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
1876 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001877 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001878};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001879
Takashi Iwaid32410b12005-11-24 16:06:23 +01001880static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
1881 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001882 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
1883 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
1884 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001885 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001886};
1887
1888static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001889 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01001890 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
1891 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
1892 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001893 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
1894 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
1895
1896 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
1897 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
1898 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
1899 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
1900 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
1901 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
1902 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
1903 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
1904
1905 HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
1906 HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
1907
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001908 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001909 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
1910
1911 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
1912 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
1913 {
1914 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1915 .name = "Channel Mode",
1916 .info = ad198x_ch_mode_info,
1917 .get = ad198x_ch_mode_get,
1918 .put = ad198x_ch_mode_put,
1919 },
1920
1921 { } /* end */
1922};
1923
1924/* laptop mode */
1925static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
1926 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1927 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
1928 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
1929
1930 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
1931 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
1932 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
1933 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
1934 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
1935 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
1936
1937 HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
1938 HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
1939
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001940 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001941 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
1942
1943 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
1944
1945 {
1946 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1947 .name = "External Amplifier",
Takashi Iwai18a815d2006-03-01 19:54:39 +01001948 .info = ad198x_eapd_info,
1949 .get = ad198x_eapd_get,
1950 .put = ad198x_eapd_put,
1951 .private_value = 0x12 | (1 << 8), /* port-D, inversed */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001952 },
1953
1954 { } /* end */
1955};
1956
1957/* capture */
1958static struct snd_kcontrol_new ad1988_capture_mixers[] = {
1959 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
1960 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
1961 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
1962 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
1963 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
1964 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
1965 {
1966 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1967 /* The multiple "Capture Source" controls confuse alsamixer
1968 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001969 */
1970 /* .name = "Capture Source", */
1971 .name = "Input Source",
1972 .count = 3,
1973 .info = ad198x_mux_enum_info,
1974 .get = ad198x_mux_enum_get,
1975 .put = ad198x_mux_enum_put,
1976 },
1977 { } /* end */
1978};
1979
1980static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
1981 struct snd_ctl_elem_info *uinfo)
1982{
1983 static char *texts[] = {
1984 "PCM", "ADC1", "ADC2", "ADC3"
1985 };
1986 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1987 uinfo->count = 1;
1988 uinfo->value.enumerated.items = 4;
1989 if (uinfo->value.enumerated.item >= 4)
1990 uinfo->value.enumerated.item = 3;
1991 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1992 return 0;
1993}
1994
1995static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
1996 struct snd_ctl_elem_value *ucontrol)
1997{
1998 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1999 unsigned int sel;
2000
Takashi Iwaibddcf542007-07-24 18:04:05 +02002001 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2002 AC_AMP_GET_INPUT);
2003 if (!(sel & 0x80))
2004 ucontrol->value.enumerated.item[0] = 0;
2005 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002006 sel = snd_hda_codec_read(codec, 0x0b, 0,
2007 AC_VERB_GET_CONNECT_SEL, 0);
2008 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002009 sel++;
2010 else
2011 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002012 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002013 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002014 return 0;
2015}
2016
2017static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2018 struct snd_ctl_elem_value *ucontrol)
2019{
2020 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002021 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002022 int change;
2023
Takashi Iwai35b26722007-05-05 12:17:17 +02002024 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002025 if (val > 3)
2026 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002027 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002028 sel = snd_hda_codec_read(codec, 0x1d, 0,
2029 AC_VERB_GET_AMP_GAIN_MUTE,
2030 AC_AMP_GET_INPUT);
2031 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002032 if (change) {
2033 snd_hda_codec_write_cache(codec, 0x1d, 0,
2034 AC_VERB_SET_AMP_GAIN_MUTE,
2035 AMP_IN_UNMUTE(0));
2036 snd_hda_codec_write_cache(codec, 0x1d, 0,
2037 AC_VERB_SET_AMP_GAIN_MUTE,
2038 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002039 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002040 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002041 sel = snd_hda_codec_read(codec, 0x1d, 0,
2042 AC_VERB_GET_AMP_GAIN_MUTE,
2043 AC_AMP_GET_INPUT | 0x01);
2044 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002045 if (change) {
2046 snd_hda_codec_write_cache(codec, 0x1d, 0,
2047 AC_VERB_SET_AMP_GAIN_MUTE,
2048 AMP_IN_MUTE(0));
2049 snd_hda_codec_write_cache(codec, 0x1d, 0,
2050 AC_VERB_SET_AMP_GAIN_MUTE,
2051 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002052 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002053 sel = snd_hda_codec_read(codec, 0x0b, 0,
2054 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2055 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002056 if (change)
2057 snd_hda_codec_write_cache(codec, 0x0b, 0,
2058 AC_VERB_SET_CONNECT_SEL,
2059 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002060 }
2061 return change;
2062}
2063
2064static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
2065 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2066 {
2067 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2068 .name = "IEC958 Playback Source",
2069 .info = ad1988_spdif_playback_source_info,
2070 .get = ad1988_spdif_playback_source_get,
2071 .put = ad1988_spdif_playback_source_put,
2072 },
2073 { } /* end */
2074};
2075
2076static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
2077 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2078 { } /* end */
2079};
2080
2081
2082/*
2083 * initialization verbs
2084 */
2085
2086/*
2087 * for 6-stack (+dig)
2088 */
2089static struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002090 /* Front, Surround, CLFE, side DAC; unmute as default */
2091 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2092 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2093 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2094 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002095 /* Port-A front headphon path */
2096 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2097 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2098 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2099 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2100 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2101 /* Port-D line-out path */
2102 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2103 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2104 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2105 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2106 /* Port-F surround path */
2107 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2108 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2109 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2110 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2111 /* Port-G CLFE path */
2112 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2113 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2114 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2115 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2116 /* Port-H side path */
2117 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2118 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2119 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2120 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2121 /* Mono out path */
2122 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2123 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2124 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2125 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2126 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2127 /* Port-B front mic-in path */
2128 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2129 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2130 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2131 /* Port-C line-in path */
2132 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2133 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2134 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2135 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2136 /* Port-E mic-in path */
2137 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2138 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2139 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2140 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002141 /* Analog CD Input */
2142 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002143
2144 { }
2145};
2146
2147static struct hda_verb ad1988_capture_init_verbs[] = {
2148 /* mute analog mix */
2149 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2150 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2151 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2152 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2153 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2154 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2155 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2156 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2157 /* select ADCs - front-mic */
2158 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2159 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2160 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
2161 /* ADCs; muted */
2162 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2163 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2164 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2165
2166 { }
2167};
2168
2169static struct hda_verb ad1988_spdif_init_verbs[] = {
2170 /* SPDIF out sel */
2171 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2172 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2173 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002174 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002175 /* SPDIF out pin */
2176 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002177
2178 { }
2179};
2180
2181/*
2182 * verbs for 3stack (+dig)
2183 */
2184static struct hda_verb ad1988_3stack_ch2_init[] = {
2185 /* set port-C to line-in */
2186 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2187 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2188 /* set port-E to mic-in */
2189 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2190 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2191 { } /* end */
2192};
2193
2194static struct hda_verb ad1988_3stack_ch6_init[] = {
2195 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002196 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002197 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002198 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002199 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002200 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002201 { } /* end */
2202};
2203
2204static struct hda_channel_mode ad1988_3stack_modes[2] = {
2205 { 2, ad1988_3stack_ch2_init },
2206 { 6, ad1988_3stack_ch6_init },
2207};
2208
2209static struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002210 /* Front, Surround, CLFE, side DAC; unmute as default */
2211 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2212 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2213 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2214 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002215 /* Port-A front headphon path */
2216 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2217 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2218 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2219 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2220 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2221 /* Port-D line-out path */
2222 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2223 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2224 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2225 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2226 /* Mono out path */
2227 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2228 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2229 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2230 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2231 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2232 /* Port-B front mic-in path */
2233 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2234 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2235 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002236 /* Port-C line-in/surround path - 6ch mode as default */
2237 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2238 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002239 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002240 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002241 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002242 /* Port-E mic-in/CLFE path - 6ch mode as default */
2243 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2244 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002245 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002246 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002247 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2248 /* mute analog mix */
2249 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2250 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2251 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2252 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2253 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2254 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2255 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2256 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2257 /* select ADCs - front-mic */
2258 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2259 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2260 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
2261 /* ADCs; muted */
2262 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2263 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2264 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2265 { }
2266};
2267
2268/*
2269 * verbs for laptop mode (+dig)
2270 */
2271static struct hda_verb ad1988_laptop_hp_on[] = {
2272 /* unmute port-A and mute port-D */
2273 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2274 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2275 { } /* end */
2276};
2277static struct hda_verb ad1988_laptop_hp_off[] = {
2278 /* mute port-A and unmute port-D */
2279 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2280 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2281 { } /* end */
2282};
2283
2284#define AD1988_HP_EVENT 0x01
2285
2286static struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002287 /* Front, Surround, CLFE, side DAC; unmute as default */
2288 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2289 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2290 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2291 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002292 /* Port-A front headphon path */
2293 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2294 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2295 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2296 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2297 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2298 /* unsolicited event for pin-sense */
2299 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2300 /* Port-D line-out path + EAPD */
2301 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2302 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2303 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2304 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2305 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2306 /* Mono out path */
2307 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2308 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2309 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2310 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2311 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2312 /* Port-B mic-in path */
2313 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2314 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2315 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2316 /* Port-C docking station - try to output */
2317 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2318 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2319 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2320 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2321 /* mute analog mix */
2322 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2323 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2324 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2325 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2326 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2327 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2328 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2329 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2330 /* select ADCs - mic */
2331 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2332 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2333 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
2334 /* ADCs; muted */
2335 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2336 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2337 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2338 { }
2339};
2340
2341static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2342{
2343 if ((res >> 26) != AD1988_HP_EVENT)
2344 return;
2345 if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31))
2346 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2347 else
2348 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2349}
2350
Takashi Iwaicb53c622007-08-10 17:21:45 +02002351#ifdef CONFIG_SND_HDA_POWER_SAVE
2352static struct hda_amp_list ad1988_loopbacks[] = {
2353 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2354 { 0x20, HDA_INPUT, 1 }, /* Line */
2355 { 0x20, HDA_INPUT, 4 }, /* Mic */
2356 { 0x20, HDA_INPUT, 6 }, /* CD */
2357 { } /* end */
2358};
2359#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002360
2361/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002362 * Automatic parse of I/O pins from the BIOS configuration
2363 */
2364
2365#define NUM_CONTROL_ALLOC 32
2366#define NUM_VERB_ALLOC 32
2367
2368enum {
2369 AD_CTL_WIDGET_VOL,
2370 AD_CTL_WIDGET_MUTE,
2371 AD_CTL_BIND_MUTE,
2372};
2373static struct snd_kcontrol_new ad1988_control_templates[] = {
2374 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2375 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2376 HDA_BIND_MUTE(NULL, 0, 0, 0),
2377};
2378
2379/* add dynamic controls */
2380static int add_control(struct ad198x_spec *spec, int type, const char *name,
2381 unsigned long val)
2382{
2383 struct snd_kcontrol_new *knew;
2384
2385 if (spec->num_kctl_used >= spec->num_kctl_alloc) {
2386 int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
2387
2388 knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
2389 if (! knew)
2390 return -ENOMEM;
2391 if (spec->kctl_alloc) {
2392 memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
2393 kfree(spec->kctl_alloc);
2394 }
2395 spec->kctl_alloc = knew;
2396 spec->num_kctl_alloc = num;
2397 }
2398
2399 knew = &spec->kctl_alloc[spec->num_kctl_used];
2400 *knew = ad1988_control_templates[type];
2401 knew->name = kstrdup(name, GFP_KERNEL);
2402 if (! knew->name)
2403 return -ENOMEM;
2404 knew->private_value = val;
2405 spec->num_kctl_used++;
2406 return 0;
2407}
2408
2409#define AD1988_PIN_CD_NID 0x18
2410#define AD1988_PIN_BEEP_NID 0x10
2411
2412static hda_nid_t ad1988_mixer_nids[8] = {
2413 /* A B C D E F G H */
2414 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2415};
2416
2417static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2418{
2419 static hda_nid_t idx_to_dac[8] = {
2420 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002421 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002422 };
2423 static hda_nid_t idx_to_dac_rev2[8] = {
2424 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002425 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002426 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002427 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002428 return idx_to_dac_rev2[idx];
2429 else
2430 return idx_to_dac[idx];
2431}
2432
2433static hda_nid_t ad1988_boost_nids[8] = {
2434 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2435};
2436
2437static int ad1988_pin_idx(hda_nid_t nid)
2438{
2439 static hda_nid_t ad1988_io_pins[8] = {
2440 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2441 };
2442 int i;
2443 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2444 if (ad1988_io_pins[i] == nid)
2445 return i;
2446 return 0; /* should be -1 */
2447}
2448
2449static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2450{
2451 static int loopback_idx[8] = {
2452 2, 0, 1, 3, 4, 5, 1, 4
2453 };
2454 switch (nid) {
2455 case AD1988_PIN_CD_NID:
2456 return 6;
2457 default:
2458 return loopback_idx[ad1988_pin_idx(nid)];
2459 }
2460}
2461
2462static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2463{
2464 static int adc_idx[8] = {
2465 0, 1, 2, 8, 4, 3, 6, 7
2466 };
2467 switch (nid) {
2468 case AD1988_PIN_CD_NID:
2469 return 5;
2470 default:
2471 return adc_idx[ad1988_pin_idx(nid)];
2472 }
2473}
2474
2475/* fill in the dac_nids table from the parsed pin configuration */
2476static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2477 const struct auto_pin_cfg *cfg)
2478{
2479 struct ad198x_spec *spec = codec->spec;
2480 int i, idx;
2481
2482 spec->multiout.dac_nids = spec->private_dac_nids;
2483
2484 /* check the pins hardwired to audio widget */
2485 for (i = 0; i < cfg->line_outs; i++) {
2486 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2487 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2488 }
2489 spec->multiout.num_dacs = cfg->line_outs;
2490 return 0;
2491}
2492
2493/* add playback controls from the parsed DAC table */
2494static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2495 const struct auto_pin_cfg *cfg)
2496{
2497 char name[32];
2498 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2499 hda_nid_t nid;
2500 int i, err;
2501
2502 for (i = 0; i < cfg->line_outs; i++) {
2503 hda_nid_t dac = spec->multiout.dac_nids[i];
2504 if (! dac)
2505 continue;
2506 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2507 if (i == 2) {
2508 /* Center/LFE */
2509 err = add_control(spec, AD_CTL_WIDGET_VOL,
2510 "Center Playback Volume",
2511 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2512 if (err < 0)
2513 return err;
2514 err = add_control(spec, AD_CTL_WIDGET_VOL,
2515 "LFE Playback Volume",
2516 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2517 if (err < 0)
2518 return err;
2519 err = add_control(spec, AD_CTL_BIND_MUTE,
2520 "Center Playback Switch",
2521 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2522 if (err < 0)
2523 return err;
2524 err = add_control(spec, AD_CTL_BIND_MUTE,
2525 "LFE Playback Switch",
2526 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2527 if (err < 0)
2528 return err;
2529 } else {
2530 sprintf(name, "%s Playback Volume", chname[i]);
2531 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2532 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2533 if (err < 0)
2534 return err;
2535 sprintf(name, "%s Playback Switch", chname[i]);
2536 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2537 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2538 if (err < 0)
2539 return err;
2540 }
2541 }
2542 return 0;
2543}
2544
2545/* add playback controls for speaker and HP outputs */
2546static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2547 const char *pfx)
2548{
2549 struct ad198x_spec *spec = codec->spec;
2550 hda_nid_t nid;
2551 int idx, err;
2552 char name[32];
2553
2554 if (! pin)
2555 return 0;
2556
2557 idx = ad1988_pin_idx(pin);
2558 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai82bc9552006-03-21 11:24:42 +01002559 /* specify the DAC as the extra output */
2560 if (! spec->multiout.hp_nid)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002561 spec->multiout.hp_nid = nid;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002562 else
2563 spec->multiout.extra_out_nid[0] = nid;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002564 /* control HP volume/switch on the output mixer amp */
2565 sprintf(name, "%s Playback Volume", pfx);
2566 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2567 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
2568 return err;
2569 nid = ad1988_mixer_nids[idx];
2570 sprintf(name, "%s Playback Switch", pfx);
2571 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2572 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2573 return err;
2574 return 0;
2575}
2576
2577/* create input playback/capture controls for the given pin */
2578static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2579 const char *ctlname, int boost)
2580{
2581 char name[32];
2582 int err, idx;
2583
2584 sprintf(name, "%s Playback Volume", ctlname);
2585 idx = ad1988_pin_to_loopback_idx(pin);
2586 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2587 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2588 return err;
2589 sprintf(name, "%s Playback Switch", ctlname);
2590 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2591 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2592 return err;
2593 if (boost) {
2594 hda_nid_t bnid;
2595 idx = ad1988_pin_idx(pin);
2596 bnid = ad1988_boost_nids[idx];
2597 if (bnid) {
2598 sprintf(name, "%s Boost", ctlname);
2599 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2600 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2601
2602 }
2603 }
2604 return 0;
2605}
2606
2607/* create playback/capture controls for input pins */
2608static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2609 const struct auto_pin_cfg *cfg)
2610{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002611 struct hda_input_mux *imux = &spec->private_imux;
2612 int i, err;
2613
2614 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002615 err = new_analog_input(spec, cfg->input_pins[i],
2616 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002617 i <= AUTO_PIN_FRONT_MIC);
2618 if (err < 0)
2619 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002620 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002621 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2622 imux->num_items++;
2623 }
2624 imux->items[imux->num_items].label = "Mix";
2625 imux->items[imux->num_items].index = 9;
2626 imux->num_items++;
2627
2628 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2629 "Analog Mix Playback Volume",
2630 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2631 return err;
2632 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2633 "Analog Mix Playback Switch",
2634 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2635 return err;
2636
2637 return 0;
2638}
2639
2640static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2641 hda_nid_t nid, int pin_type,
2642 int dac_idx)
2643{
2644 /* set as output */
2645 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2646 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2647 switch (nid) {
2648 case 0x11: /* port-A - DAC 04 */
2649 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2650 break;
2651 case 0x14: /* port-B - DAC 06 */
2652 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2653 break;
2654 case 0x15: /* port-C - DAC 05 */
2655 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2656 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002657 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002658 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2659 break;
2660 case 0x13: /* mono - DAC 04 */
2661 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2662 break;
2663 }
2664}
2665
2666static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2667{
2668 struct ad198x_spec *spec = codec->spec;
2669 int i;
2670
2671 for (i = 0; i < spec->autocfg.line_outs; i++) {
2672 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2673 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2674 }
2675}
2676
2677static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2678{
2679 struct ad198x_spec *spec = codec->spec;
2680 hda_nid_t pin;
2681
Takashi Iwai82bc9552006-03-21 11:24:42 +01002682 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002683 if (pin) /* connect to front */
2684 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002685 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002686 if (pin) /* connect to front */
2687 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2688}
2689
2690static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2691{
2692 struct ad198x_spec *spec = codec->spec;
2693 int i, idx;
2694
2695 for (i = 0; i < AUTO_PIN_LAST; i++) {
2696 hda_nid_t nid = spec->autocfg.input_pins[i];
2697 if (! nid)
2698 continue;
2699 switch (nid) {
2700 case 0x15: /* port-C */
2701 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2702 break;
2703 case 0x17: /* port-E */
2704 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2705 break;
2706 }
2707 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2708 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2709 if (nid != AD1988_PIN_CD_NID)
2710 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
2711 AMP_OUT_MUTE);
2712 idx = ad1988_pin_idx(nid);
2713 if (ad1988_boost_nids[idx])
2714 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
2715 AC_VERB_SET_AMP_GAIN_MUTE,
2716 AMP_OUT_ZERO);
2717 }
2718}
2719
2720/* parse the BIOS configuration and set up the alc_spec */
2721/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
2722static int ad1988_parse_auto_config(struct hda_codec *codec)
2723{
2724 struct ad198x_spec *spec = codec->spec;
2725 int err;
2726
Kailang Yangdf694da2005-12-05 19:42:22 +01002727 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002728 return err;
2729 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
2730 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002731 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002732 return 0; /* can't find valid BIOS pin config */
2733 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01002734 (err = ad1988_auto_create_extra_out(codec,
2735 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002736 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002737 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002738 "Headphone")) < 0 ||
2739 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
2740 return err;
2741
2742 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2743
2744 if (spec->autocfg.dig_out_pin)
2745 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2746 if (spec->autocfg.dig_in_pin)
2747 spec->dig_in_nid = AD1988_SPDIF_IN;
2748
2749 if (spec->kctl_alloc)
2750 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
2751
2752 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
2753
2754 spec->input_mux = &spec->private_imux;
2755
2756 return 1;
2757}
2758
2759/* init callback for auto-configuration model -- overriding the default init */
2760static int ad1988_auto_init(struct hda_codec *codec)
2761{
2762 ad198x_init(codec);
2763 ad1988_auto_init_multi_out(codec);
2764 ad1988_auto_init_extra_out(codec);
2765 ad1988_auto_init_analog_input(codec);
2766 return 0;
2767}
2768
2769
2770/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002771 */
2772
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002773static const char *ad1988_models[AD1988_MODEL_LAST] = {
2774 [AD1988_6STACK] = "6stack",
2775 [AD1988_6STACK_DIG] = "6stack-dig",
2776 [AD1988_3STACK] = "3stack",
2777 [AD1988_3STACK_DIG] = "3stack-dig",
2778 [AD1988_LAPTOP] = "laptop",
2779 [AD1988_LAPTOP_DIG] = "laptop-dig",
2780 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002781};
2782
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002783static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002784 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002785 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002786 {}
2787};
2788
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002789static int patch_ad1988(struct hda_codec *codec)
2790{
2791 struct ad198x_spec *spec;
2792 int board_config;
2793
2794 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2795 if (spec == NULL)
2796 return -ENOMEM;
2797
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002798 codec->spec = spec;
2799
Takashi Iwai1a806f42006-07-03 15:58:16 +02002800 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002801 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
2802
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002803 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002804 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002805 if (board_config < 0) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002806 printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n");
2807 board_config = AD1988_AUTO;
2808 }
2809
2810 if (board_config == AD1988_AUTO) {
2811 /* automatic parse from the BIOS config */
2812 int err = ad1988_parse_auto_config(codec);
2813 if (err < 0) {
2814 ad198x_free(codec);
2815 return err;
2816 } else if (! err) {
2817 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
2818 board_config = AD1988_6STACK;
2819 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002820 }
2821
2822 switch (board_config) {
2823 case AD1988_6STACK:
2824 case AD1988_6STACK_DIG:
2825 spec->multiout.max_channels = 8;
2826 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002827 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002828 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
2829 else
2830 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002831 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002832 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002833 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002834 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
2835 else
2836 spec->mixers[0] = ad1988_6stack_mixers1;
2837 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002838 spec->num_init_verbs = 1;
2839 spec->init_verbs[0] = ad1988_6stack_init_verbs;
2840 if (board_config == AD1988_6STACK_DIG) {
2841 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2842 spec->dig_in_nid = AD1988_SPDIF_IN;
2843 }
2844 break;
2845 case AD1988_3STACK:
2846 case AD1988_3STACK_DIG:
2847 spec->multiout.max_channels = 6;
2848 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002849 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002850 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
2851 else
2852 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002853 spec->input_mux = &ad1988_6stack_capture_source;
2854 spec->channel_mode = ad1988_3stack_modes;
2855 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002856 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002857 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002858 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
2859 else
2860 spec->mixers[0] = ad1988_3stack_mixers1;
2861 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002862 spec->num_init_verbs = 1;
2863 spec->init_verbs[0] = ad1988_3stack_init_verbs;
2864 if (board_config == AD1988_3STACK_DIG)
2865 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2866 break;
2867 case AD1988_LAPTOP:
2868 case AD1988_LAPTOP_DIG:
2869 spec->multiout.max_channels = 2;
2870 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002871 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002872 spec->input_mux = &ad1988_laptop_capture_source;
2873 spec->num_mixers = 1;
2874 spec->mixers[0] = ad1988_laptop_mixers;
2875 spec->num_init_verbs = 1;
2876 spec->init_verbs[0] = ad1988_laptop_init_verbs;
2877 if (board_config == AD1988_LAPTOP_DIG)
2878 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2879 break;
2880 }
2881
Takashi Iwaid32410b12005-11-24 16:06:23 +01002882 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
2883 spec->adc_nids = ad1988_adc_nids;
2884 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002885 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
2886 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
2887 if (spec->multiout.dig_out_nid) {
2888 spec->mixers[spec->num_mixers++] = ad1988_spdif_out_mixers;
2889 spec->init_verbs[spec->num_init_verbs++] = ad1988_spdif_init_verbs;
2890 }
2891 if (spec->dig_in_nid)
2892 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
2893
2894 codec->patch_ops = ad198x_patch_ops;
2895 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002896 case AD1988_AUTO:
2897 codec->patch_ops.init = ad1988_auto_init;
2898 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002899 case AD1988_LAPTOP:
2900 case AD1988_LAPTOP_DIG:
2901 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
2902 break;
2903 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02002904#ifdef CONFIG_SND_HDA_POWER_SAVE
2905 spec->loopback.amplist = ad1988_loopbacks;
2906#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01002907 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002908
2909 return 0;
2910}
2911
2912
2913/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02002914 * AD1884 / AD1984
2915 *
2916 * port-B - front line/mic-in
2917 * port-E - aux in/out
2918 * port-F - aux in/out
2919 * port-C - rear line/mic-in
2920 * port-D - rear line/hp-out
2921 * port-A - front line/hp-out
2922 *
2923 * AD1984 = AD1884 + two digital mic-ins
2924 *
2925 * FIXME:
2926 * For simplicity, we share the single DAC for both HP and line-outs
2927 * right now. The inidividual playbacks could be easily implemented,
2928 * but no build-up framework is given, so far.
2929 */
2930
2931static hda_nid_t ad1884_dac_nids[1] = {
2932 0x04,
2933};
2934
2935static hda_nid_t ad1884_adc_nids[2] = {
2936 0x08, 0x09,
2937};
2938
2939static hda_nid_t ad1884_capsrc_nids[2] = {
2940 0x0c, 0x0d,
2941};
2942
2943#define AD1884_SPDIF_OUT 0x02
2944
2945static struct hda_input_mux ad1884_capture_source = {
2946 .num_items = 4,
2947 .items = {
2948 { "Front Mic", 0x0 },
2949 { "Mic", 0x1 },
2950 { "CD", 0x2 },
2951 { "Mix", 0x3 },
2952 },
2953};
2954
2955static struct snd_kcontrol_new ad1884_base_mixers[] = {
2956 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2957 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
2958 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
2959 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
2960 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
2961 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
2962 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
2963 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
2964 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
2965 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
2966 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
2967 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
2968 /*
2969 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
2970 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
2971 HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
2972 HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
2973 */
2974 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
2975 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
2976 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2977 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2978 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2979 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2980 {
2981 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2982 /* The multiple "Capture Source" controls confuse alsamixer
2983 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02002984 */
2985 /* .name = "Capture Source", */
2986 .name = "Input Source",
2987 .count = 2,
2988 .info = ad198x_mux_enum_info,
2989 .get = ad198x_mux_enum_get,
2990 .put = ad198x_mux_enum_put,
2991 },
2992 /* SPDIF controls */
2993 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2994 {
2995 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2996 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
2997 /* identical with ad1983 */
2998 .info = ad1983_spdif_route_info,
2999 .get = ad1983_spdif_route_get,
3000 .put = ad1983_spdif_route_put,
3001 },
3002 { } /* end */
3003};
3004
3005static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3006 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3007 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3008 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003009 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003010 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003011 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003012 { } /* end */
3013};
3014
3015/*
3016 * initialization verbs
3017 */
3018static struct hda_verb ad1884_init_verbs[] = {
3019 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003020 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3021 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003022 /* Port-A (HP) mixer */
3023 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3024 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3025 /* Port-A pin */
3026 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3027 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3028 /* HP selector - select DAC2 */
3029 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3030 /* Port-D (Line-out) mixer */
3031 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3032 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3033 /* Port-D pin */
3034 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3035 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3036 /* Mono-out mixer */
3037 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3038 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3039 /* Mono-out pin */
3040 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3041 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3042 /* Mono selector */
3043 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3044 /* Port-B (front mic) pin */
3045 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3046 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3047 /* Port-C (rear mic) pin */
3048 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3049 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3050 /* Analog mixer; mute as default */
3051 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3052 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3053 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3054 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3055 /* Analog Mix output amp */
3056 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3057 /* SPDIF output selector */
3058 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3059 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3060 { } /* end */
3061};
3062
Takashi Iwaicb53c622007-08-10 17:21:45 +02003063#ifdef CONFIG_SND_HDA_POWER_SAVE
3064static struct hda_amp_list ad1884_loopbacks[] = {
3065 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3066 { 0x20, HDA_INPUT, 1 }, /* Mic */
3067 { 0x20, HDA_INPUT, 2 }, /* CD */
3068 { 0x20, HDA_INPUT, 4 }, /* Docking */
3069 { } /* end */
3070};
3071#endif
3072
Takashi Iwai2134ea42008-01-10 16:53:55 +01003073static const char *ad1884_slave_vols[] = {
3074 "PCM Playback Volume",
3075 "Mic Playback Volume",
3076 "Mono Playback Volume",
3077 "Front Mic Playback Volume",
3078 "Mic Playback Volume",
3079 "CD Playback Volume",
3080 "Internal Mic Playback Volume",
3081 "Docking Mic Playback Volume"
3082 "Beep Playback Volume",
Takashi Iwai4806ef02008-01-26 09:58:13 +01003083 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003084 NULL
3085};
3086
Takashi Iwai2bac6472007-05-18 18:21:41 +02003087static int patch_ad1884(struct hda_codec *codec)
3088{
3089 struct ad198x_spec *spec;
3090
3091 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3092 if (spec == NULL)
3093 return -ENOMEM;
3094
3095 mutex_init(&spec->amp_mutex);
3096 codec->spec = spec;
3097
3098 spec->multiout.max_channels = 2;
3099 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3100 spec->multiout.dac_nids = ad1884_dac_nids;
3101 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3102 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3103 spec->adc_nids = ad1884_adc_nids;
3104 spec->capsrc_nids = ad1884_capsrc_nids;
3105 spec->input_mux = &ad1884_capture_source;
3106 spec->num_mixers = 1;
3107 spec->mixers[0] = ad1884_base_mixers;
3108 spec->num_init_verbs = 1;
3109 spec->init_verbs[0] = ad1884_init_verbs;
3110 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003111#ifdef CONFIG_SND_HDA_POWER_SAVE
3112 spec->loopback.amplist = ad1884_loopbacks;
3113#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003114 spec->vmaster_nid = 0x04;
3115 /* we need to cover all playback volumes */
3116 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003117
3118 codec->patch_ops = ad198x_patch_ops;
3119
3120 return 0;
3121}
3122
3123/*
3124 * Lenovo Thinkpad T61/X61
3125 */
3126static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwai3b194402007-06-04 18:32:23 +02003127 .num_items = 3,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003128 .items = {
3129 { "Mic", 0x0 },
3130 { "Internal Mic", 0x1 },
3131 { "Mix", 0x3 },
3132 },
3133};
3134
3135static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3136 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3137 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3138 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3139 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3140 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3141 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3142 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3143 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3144 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3145 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003146 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3147 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003148 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003149 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3150 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003151 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3152 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3153 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3154 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3155 {
3156 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3157 /* The multiple "Capture Source" controls confuse alsamixer
3158 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003159 */
3160 /* .name = "Capture Source", */
3161 .name = "Input Source",
3162 .count = 2,
3163 .info = ad198x_mux_enum_info,
3164 .get = ad198x_mux_enum_get,
3165 .put = ad198x_mux_enum_put,
3166 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003167 /* SPDIF controls */
3168 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3169 {
3170 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3171 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3172 /* identical with ad1983 */
3173 .info = ad1983_spdif_route_info,
3174 .get = ad1983_spdif_route_get,
3175 .put = ad1983_spdif_route_put,
3176 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003177 { } /* end */
3178};
3179
3180/* additional verbs */
3181static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3182 /* Port-E (docking station mic) pin */
3183 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3184 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3185 /* docking mic boost */
3186 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3187 /* Analog mixer - docking mic; mute as default */
3188 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003189 /* enable EAPD bit */
3190 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003191 { } /* end */
3192};
3193
3194/* Digial MIC ADC NID 0x05 + 0x06 */
3195static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3196 struct hda_codec *codec,
3197 unsigned int stream_tag,
3198 unsigned int format,
3199 struct snd_pcm_substream *substream)
3200{
3201 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3202 stream_tag, 0, format);
3203 return 0;
3204}
3205
3206static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3207 struct hda_codec *codec,
3208 struct snd_pcm_substream *substream)
3209{
3210 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3211 0, 0, 0);
3212 return 0;
3213}
3214
3215static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3216 .substreams = 2,
3217 .channels_min = 2,
3218 .channels_max = 2,
3219 .nid = 0x05,
3220 .ops = {
3221 .prepare = ad1984_pcm_dmic_prepare,
3222 .cleanup = ad1984_pcm_dmic_cleanup
3223 },
3224};
3225
3226static int ad1984_build_pcms(struct hda_codec *codec)
3227{
3228 struct ad198x_spec *spec = codec->spec;
3229 struct hda_pcm *info;
3230 int err;
3231
3232 err = ad198x_build_pcms(codec);
3233 if (err < 0)
3234 return err;
3235
3236 info = spec->pcm_rec + codec->num_pcms;
3237 codec->num_pcms++;
3238 info->name = "AD1984 Digital Mic";
3239 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3240 return 0;
3241}
3242
3243/* models */
3244enum {
3245 AD1984_BASIC,
3246 AD1984_THINKPAD,
3247 AD1984_MODELS
3248};
3249
3250static const char *ad1984_models[AD1984_MODELS] = {
3251 [AD1984_BASIC] = "basic",
3252 [AD1984_THINKPAD] = "thinkpad",
3253};
3254
3255static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3256 /* Lenovo Thinkpad T61/X61 */
3257 SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
3258 {}
3259};
3260
3261static int patch_ad1984(struct hda_codec *codec)
3262{
3263 struct ad198x_spec *spec;
3264 int board_config, err;
3265
3266 err = patch_ad1884(codec);
3267 if (err < 0)
3268 return err;
3269 spec = codec->spec;
3270 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3271 ad1984_models, ad1984_cfg_tbl);
3272 switch (board_config) {
3273 case AD1984_BASIC:
3274 /* additional digital mics */
3275 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3276 codec->patch_ops.build_pcms = ad1984_build_pcms;
3277 break;
3278 case AD1984_THINKPAD:
Jerone Youngebf00c52008-01-07 12:22:18 +01003279 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003280 spec->input_mux = &ad1984_thinkpad_capture_source;
3281 spec->mixers[0] = ad1984_thinkpad_mixers;
3282 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
3283 break;
3284 }
3285 return 0;
3286}
3287
3288
3289/*
Takashi Iwai0ac85512007-06-20 15:46:13 +02003290 * AD1882
3291 *
3292 * port-A - front hp-out
3293 * port-B - front mic-in
3294 * port-C - rear line-in, shared surr-out (3stack)
3295 * port-D - rear line-out
3296 * port-E - rear mic-in, shared clfe-out (3stack)
3297 * port-F - rear surr-out (6stack)
3298 * port-G - rear clfe-out (6stack)
3299 */
3300
3301static hda_nid_t ad1882_dac_nids[3] = {
3302 0x04, 0x03, 0x05
3303};
3304
3305static hda_nid_t ad1882_adc_nids[2] = {
3306 0x08, 0x09,
3307};
3308
3309static hda_nid_t ad1882_capsrc_nids[2] = {
3310 0x0c, 0x0d,
3311};
3312
3313#define AD1882_SPDIF_OUT 0x02
3314
3315/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
3316static struct hda_input_mux ad1882_capture_source = {
3317 .num_items = 5,
3318 .items = {
3319 { "Front Mic", 0x1 },
3320 { "Mic", 0x4 },
3321 { "Line", 0x2 },
3322 { "CD", 0x3 },
3323 { "Mix", 0x7 },
3324 },
3325};
3326
3327static struct snd_kcontrol_new ad1882_base_mixers[] = {
3328 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3329 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
3330 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
3331 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
3332 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3333 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3334 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3335 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3336 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3337 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3338 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3339 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3340 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
3341 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
3342 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
3343 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
3344 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
3345 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
3346 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
3347 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
3348 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
3349 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3350 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3351 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3352 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3353 {
3354 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3355 /* The multiple "Capture Source" controls confuse alsamixer
3356 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02003357 */
3358 /* .name = "Capture Source", */
3359 .name = "Input Source",
3360 .count = 2,
3361 .info = ad198x_mux_enum_info,
3362 .get = ad198x_mux_enum_get,
3363 .put = ad198x_mux_enum_put,
3364 },
3365 /* SPDIF controls */
3366 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3367 {
3368 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3369 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3370 /* identical with ad1983 */
3371 .info = ad1983_spdif_route_info,
3372 .get = ad1983_spdif_route_get,
3373 .put = ad1983_spdif_route_put,
3374 },
3375 { } /* end */
3376};
3377
3378static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
3379 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
3380 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
3381 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
3382 {
3383 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3384 .name = "Channel Mode",
3385 .info = ad198x_ch_mode_info,
3386 .get = ad198x_ch_mode_get,
3387 .put = ad198x_ch_mode_put,
3388 },
3389 { } /* end */
3390};
3391
3392static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
3393 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
3394 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
3395 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
3396 { } /* end */
3397};
3398
3399static struct hda_verb ad1882_ch2_init[] = {
3400 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3401 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3402 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3403 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3404 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3405 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3406 { } /* end */
3407};
3408
3409static struct hda_verb ad1882_ch4_init[] = {
3410 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3411 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3412 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3413 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3414 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3415 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3416 { } /* end */
3417};
3418
3419static struct hda_verb ad1882_ch6_init[] = {
3420 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3421 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3422 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3423 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3424 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3425 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3426 { } /* end */
3427};
3428
3429static struct hda_channel_mode ad1882_modes[3] = {
3430 { 2, ad1882_ch2_init },
3431 { 4, ad1882_ch4_init },
3432 { 6, ad1882_ch6_init },
3433};
3434
3435/*
3436 * initialization verbs
3437 */
3438static struct hda_verb ad1882_init_verbs[] = {
3439 /* DACs; mute as default */
3440 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3441 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3442 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3443 /* Port-A (HP) mixer */
3444 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3445 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3446 /* Port-A pin */
3447 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3448 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3449 /* HP selector - select DAC2 */
3450 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
3451 /* Port-D (Line-out) mixer */
3452 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3453 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3454 /* Port-D pin */
3455 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3456 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3457 /* Mono-out mixer */
3458 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3459 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3460 /* Mono-out pin */
3461 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3462 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3463 /* Port-B (front mic) pin */
3464 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3465 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3466 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
3467 /* Port-C (line-in) pin */
3468 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3469 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3470 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
3471 /* Port-C mixer - mute as input */
3472 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3473 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3474 /* Port-E (mic-in) pin */
3475 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3476 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3477 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
3478 /* Port-E mixer - mute as input */
3479 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3480 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3481 /* Port-F (surround) */
3482 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3483 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3484 /* Port-G (CLFE) */
3485 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3486 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3487 /* Analog mixer; mute as default */
3488 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
3489 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3490 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3491 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3492 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3493 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3494 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3495 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
3496 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
3497 /* Analog Mix output amp */
3498 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3499 /* SPDIF output selector */
3500 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3501 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3502 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3503 { } /* end */
3504};
3505
Takashi Iwaicb53c622007-08-10 17:21:45 +02003506#ifdef CONFIG_SND_HDA_POWER_SAVE
3507static struct hda_amp_list ad1882_loopbacks[] = {
3508 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3509 { 0x20, HDA_INPUT, 1 }, /* Mic */
3510 { 0x20, HDA_INPUT, 4 }, /* Line */
3511 { 0x20, HDA_INPUT, 6 }, /* CD */
3512 { } /* end */
3513};
3514#endif
3515
Takashi Iwai0ac85512007-06-20 15:46:13 +02003516/* models */
3517enum {
3518 AD1882_3STACK,
3519 AD1882_6STACK,
3520 AD1882_MODELS
3521};
3522
3523static const char *ad1882_models[AD1986A_MODELS] = {
3524 [AD1882_3STACK] = "3stack",
3525 [AD1882_6STACK] = "6stack",
3526};
3527
3528
3529static int patch_ad1882(struct hda_codec *codec)
3530{
3531 struct ad198x_spec *spec;
3532 int board_config;
3533
3534 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3535 if (spec == NULL)
3536 return -ENOMEM;
3537
3538 mutex_init(&spec->amp_mutex);
3539 codec->spec = spec;
3540
3541 spec->multiout.max_channels = 6;
3542 spec->multiout.num_dacs = 3;
3543 spec->multiout.dac_nids = ad1882_dac_nids;
3544 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
3545 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
3546 spec->adc_nids = ad1882_adc_nids;
3547 spec->capsrc_nids = ad1882_capsrc_nids;
3548 spec->input_mux = &ad1882_capture_source;
3549 spec->num_mixers = 1;
3550 spec->mixers[0] = ad1882_base_mixers;
3551 spec->num_init_verbs = 1;
3552 spec->init_verbs[0] = ad1882_init_verbs;
3553 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003554#ifdef CONFIG_SND_HDA_POWER_SAVE
3555 spec->loopback.amplist = ad1882_loopbacks;
3556#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003557 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02003558
3559 codec->patch_ops = ad198x_patch_ops;
3560
3561 /* override some parameters */
3562 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
3563 ad1882_models, NULL);
3564 switch (board_config) {
3565 default:
3566 case AD1882_3STACK:
3567 spec->num_mixers = 2;
3568 spec->mixers[1] = ad1882_3stack_mixers;
3569 spec->channel_mode = ad1882_modes;
3570 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
3571 spec->need_dac_fix = 1;
3572 spec->multiout.max_channels = 2;
3573 spec->multiout.num_dacs = 1;
3574 break;
3575 case AD1882_6STACK:
3576 spec->num_mixers = 2;
3577 spec->mixers[1] = ad1882_6stack_mixers;
3578 break;
3579 }
3580 return 0;
3581}
3582
3583
3584/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07003585 * patch entries
3586 */
3587struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02003588 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003589 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02003590 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
3591 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003592 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07003593 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003594 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02003595 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07003596 {} /* terminator */
3597};