blob: 3a8f00e6c994bbedb5d6ff181f40a4441574c395 [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 Iwai2134ea42008-01-10 16:53:55 +0100142 NULL
143};
144
145static const char *ad_slave_sws[] = {
146 "Front Playback Switch",
147 "Surround Playback Switch",
148 "Center Playback Switch",
149 "LFE Playback Switch",
150 "Side Playback Switch",
151 "Headphone Playback Switch",
152 "Mono Playback Switch",
Takashi Iwai628ed132008-01-25 11:56:57 +0100153 "Speaker Playback Switch",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100154 NULL
155};
156
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200157static int ad198x_build_controls(struct hda_codec *codec)
158{
159 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100160 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200161 int err;
162
Takashi Iwai985be542005-11-02 18:26:49 +0100163 for (i = 0; i < spec->num_mixers; i++) {
164 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
165 if (err < 0)
166 return err;
167 }
168 if (spec->multiout.dig_out_nid) {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200169 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100170 if (err < 0)
171 return err;
172 }
173 if (spec->dig_in_nid) {
174 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
175 if (err < 0)
176 return err;
177 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100178
179 /* if we have no master control, let's create it */
180 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
181 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
182 HDA_OUTPUT, spec->vmaster_tlv);
183 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
184 spec->vmaster_tlv,
185 (spec->slave_vols ?
186 spec->slave_vols : ad_slave_vols));
187 if (err < 0)
188 return err;
189 }
190 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
191 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
192 NULL,
193 (spec->slave_sws ?
194 spec->slave_sws : ad_slave_sws));
195 if (err < 0)
196 return err;
197 }
198
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200199 return 0;
200}
201
Takashi Iwaicb53c622007-08-10 17:21:45 +0200202#ifdef CONFIG_SND_HDA_POWER_SAVE
203static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
204{
205 struct ad198x_spec *spec = codec->spec;
206 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
207}
208#endif
209
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200210/*
211 * Analog playback callbacks
212 */
213static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
214 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100215 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200216{
217 struct ad198x_spec *spec = codec->spec;
218 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
219}
220
221static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
222 struct hda_codec *codec,
223 unsigned int stream_tag,
224 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100225 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200226{
227 struct ad198x_spec *spec = codec->spec;
228 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
229 format, substream);
230}
231
232static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
233 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100234 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200235{
236 struct ad198x_spec *spec = codec->spec;
237 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
238}
239
240/*
241 * Digital out
242 */
243static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
244 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100245 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200246{
247 struct ad198x_spec *spec = codec->spec;
248 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
249}
250
251static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
252 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100253 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200254{
255 struct ad198x_spec *spec = codec->spec;
256 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
257}
258
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200259static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
260 struct hda_codec *codec,
261 unsigned int stream_tag,
262 unsigned int format,
263 struct snd_pcm_substream *substream)
264{
265 struct ad198x_spec *spec = codec->spec;
266 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
267 format, substream);
268}
269
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200270/*
271 * Analog capture
272 */
273static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
274 struct hda_codec *codec,
275 unsigned int stream_tag,
276 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100277 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200278{
279 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100280 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
281 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200282 return 0;
283}
284
285static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
286 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100287 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200288{
289 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100290 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
291 0, 0, 0);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200292 return 0;
293}
294
295
296/*
297 */
298static struct hda_pcm_stream ad198x_pcm_analog_playback = {
299 .substreams = 1,
300 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100301 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200302 .nid = 0, /* fill later */
303 .ops = {
304 .open = ad198x_playback_pcm_open,
305 .prepare = ad198x_playback_pcm_prepare,
306 .cleanup = ad198x_playback_pcm_cleanup
307 },
308};
309
310static struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100311 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200312 .channels_min = 2,
313 .channels_max = 2,
314 .nid = 0, /* fill later */
315 .ops = {
316 .prepare = ad198x_capture_pcm_prepare,
317 .cleanup = ad198x_capture_pcm_cleanup
318 },
319};
320
321static struct hda_pcm_stream ad198x_pcm_digital_playback = {
322 .substreams = 1,
323 .channels_min = 2,
324 .channels_max = 2,
325 .nid = 0, /* fill later */
326 .ops = {
327 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200328 .close = ad198x_dig_playback_pcm_close,
329 .prepare = ad198x_dig_playback_pcm_prepare
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200330 },
331};
332
Takashi Iwai985be542005-11-02 18:26:49 +0100333static struct hda_pcm_stream ad198x_pcm_digital_capture = {
334 .substreams = 1,
335 .channels_min = 2,
336 .channels_max = 2,
337 /* NID is set in alc_build_pcms */
338};
339
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200340static int ad198x_build_pcms(struct hda_codec *codec)
341{
342 struct ad198x_spec *spec = codec->spec;
343 struct hda_pcm *info = spec->pcm_rec;
344
345 codec->num_pcms = 1;
346 codec->pcm_info = info;
347
348 info->name = "AD198x Analog";
349 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
350 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
351 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
352 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100353 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
354 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200355
356 if (spec->multiout.dig_out_nid) {
357 info++;
358 codec->num_pcms++;
359 info->name = "AD198x Digital";
360 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
361 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100362 if (spec->dig_in_nid) {
363 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
364 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
365 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200366 }
367
368 return 0;
369}
370
371static void ad198x_free(struct hda_codec *codec)
372{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100373 struct ad198x_spec *spec = codec->spec;
374 unsigned int i;
375
376 if (spec->kctl_alloc) {
377 for (i = 0; i < spec->num_kctl_used; i++)
378 kfree(spec->kctl_alloc[i].name);
379 kfree(spec->kctl_alloc);
380 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200381 kfree(codec->spec);
382}
383
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200384static struct hda_codec_ops ad198x_patch_ops = {
385 .build_controls = ad198x_build_controls,
386 .build_pcms = ad198x_build_pcms,
387 .init = ad198x_init,
388 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200389#ifdef CONFIG_SND_HDA_POWER_SAVE
390 .check_power_status = ad198x_check_power_status,
391#endif
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200392};
393
394
395/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100396 * EAPD control
397 * the private value = nid | (invert << 8)
398 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200399#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100400
401static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
402 struct snd_ctl_elem_value *ucontrol)
403{
404 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
405 struct ad198x_spec *spec = codec->spec;
406 int invert = (kcontrol->private_value >> 8) & 1;
407 if (invert)
408 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
409 else
410 ucontrol->value.integer.value[0] = spec->cur_eapd;
411 return 0;
412}
413
414static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
415 struct snd_ctl_elem_value *ucontrol)
416{
417 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
418 struct ad198x_spec *spec = codec->spec;
419 int invert = (kcontrol->private_value >> 8) & 1;
420 hda_nid_t nid = kcontrol->private_value & 0xff;
421 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100422 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwai18a815d2006-03-01 19:54:39 +0100423 if (invert)
424 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200425 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100426 return 0;
427 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200428 snd_hda_codec_write_cache(codec, nid,
429 0, AC_VERB_SET_EAPD_BTLENABLE,
430 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100431 return 1;
432}
433
Takashi Iwai9230d212006-03-13 13:49:49 +0100434static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
435 struct snd_ctl_elem_info *uinfo);
436static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
437 struct snd_ctl_elem_value *ucontrol);
438static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
439 struct snd_ctl_elem_value *ucontrol);
440
441
Takashi Iwai18a815d2006-03-01 19:54:39 +0100442/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200443 * AD1986A specific
444 */
445
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446#define AD1986A_SPDIF_OUT 0x02
447#define AD1986A_FRONT_DAC 0x03
448#define AD1986A_SURR_DAC 0x04
449#define AD1986A_CLFE_DAC 0x05
450#define AD1986A_ADC 0x06
451
452static hda_nid_t ad1986a_dac_nids[3] = {
453 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
454};
Takashi Iwai985be542005-11-02 18:26:49 +0100455static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +0100456static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457
458static struct hda_input_mux ad1986a_capture_source = {
459 .num_items = 7,
460 .items = {
461 { "Mic", 0x0 },
462 { "CD", 0x1 },
463 { "Aux", 0x3 },
464 { "Line", 0x4 },
465 { "Mix", 0x5 },
466 { "Mono", 0x6 },
467 { "Phone", 0x7 },
468 },
469};
470
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471
Takashi Iwai532d5382007-07-27 19:02:40 +0200472static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
473 .ops = &snd_hda_bind_vol,
474 .values = {
475 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
476 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
477 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
478 0
479 },
480};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481
Takashi Iwai532d5382007-07-27 19:02:40 +0200482static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
483 .ops = &snd_hda_bind_sw,
484 .values = {
485 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
486 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
487 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
488 0
489 },
490};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491
492/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 * mixers
494 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100495static struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200496 /*
497 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
498 */
499 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
500 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
502 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
503 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
504 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
505 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
506 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
507 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
508 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
509 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
510 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
511 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
512 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
513 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
514 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
515 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
516 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
517 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
518 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100519 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
521 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
522 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
523 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
524 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
525 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
526 {
527 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
528 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200529 .info = ad198x_mux_enum_info,
530 .get = ad198x_mux_enum_get,
531 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 },
533 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
534 { } /* end */
535};
536
Takashi Iwai9230d212006-03-13 13:49:49 +0100537/* additional mixers for 3stack mode */
538static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
539 {
540 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
541 .name = "Channel Mode",
542 .info = ad198x_ch_mode_info,
543 .get = ad198x_ch_mode_get,
544 .put = ad198x_ch_mode_put,
545 },
546 { } /* end */
547};
548
549/* laptop model - 2ch only */
550static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
551
Takashi Iwai20a45e82007-08-15 22:20:45 +0200552/* master controls both pins 0x1a and 0x1b */
553static struct hda_bind_ctls ad1986a_laptop_master_vol = {
554 .ops = &snd_hda_bind_vol,
555 .values = {
556 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
557 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
558 0,
559 },
560};
561
562static struct hda_bind_ctls ad1986a_laptop_master_sw = {
563 .ops = &snd_hda_bind_sw,
564 .values = {
565 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
566 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
567 0,
568 },
569};
570
Takashi Iwai9230d212006-03-13 13:49:49 +0100571static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
572 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
573 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200574 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
575 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100576 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
577 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
578 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
579 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
580 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
581 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
582 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
583 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100584 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai9230d212006-03-13 13:49:49 +0100585 /* HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
586 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
587 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
588 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
589 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
590 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
591 {
592 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
593 .name = "Capture Source",
594 .info = ad198x_mux_enum_info,
595 .get = ad198x_mux_enum_get,
596 .put = ad198x_mux_enum_put,
597 },
598 { } /* end */
599};
600
Takashi Iwai825aa972006-03-17 10:50:49 +0100601/* laptop-eapd model - 2ch only */
602
Takashi Iwai825aa972006-03-17 10:50:49 +0100603static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
604 .num_items = 3,
605 .items = {
606 { "Mic", 0x0 },
607 { "Internal Mic", 0x4 },
608 { "Mix", 0x5 },
609 },
610};
611
612static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200613 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
614 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai825aa972006-03-17 10:50:49 +0100615 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
616 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
617 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT),
618 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT),
619 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
620 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100621 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai825aa972006-03-17 10:50:49 +0100622 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
623 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
624 {
625 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
626 .name = "Capture Source",
627 .info = ad198x_mux_enum_info,
628 .get = ad198x_mux_enum_get,
629 .put = ad198x_mux_enum_put,
630 },
631 {
632 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
633 .name = "External Amplifier",
634 .info = ad198x_eapd_info,
635 .get = ad198x_eapd_get,
636 .put = ad198x_eapd_put,
637 .private_value = 0x1b | (1 << 8), /* port-D, inversed */
638 },
639 { } /* end */
640};
641
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200642/* laptop-automute - 2ch only */
643
644static void ad1986a_update_hp(struct hda_codec *codec)
645{
646 struct ad198x_spec *spec = codec->spec;
647 unsigned int mute;
648
649 if (spec->jack_present)
650 mute = HDA_AMP_MUTE; /* mute internal speaker */
651 else
652 /* unmute internal speaker if necessary */
653 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
654 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
655 HDA_AMP_MUTE, mute);
656}
657
658static void ad1986a_hp_automute(struct hda_codec *codec)
659{
660 struct ad198x_spec *spec = codec->spec;
661 unsigned int present;
662
663 present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0);
Takashi Iwai53eb1b82007-10-17 10:09:32 +0200664 /* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
665 spec->jack_present = !(present & 0x80000000);
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200666 ad1986a_update_hp(codec);
667}
668
669#define AD1986A_HP_EVENT 0x37
670
671static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
672{
673 if ((res >> 26) != AD1986A_HP_EVENT)
674 return;
675 ad1986a_hp_automute(codec);
676}
677
678static int ad1986a_hp_init(struct hda_codec *codec)
679{
680 ad198x_init(codec);
681 ad1986a_hp_automute(codec);
682 return 0;
683}
684
685/* bind hp and internal speaker mute (with plug check) */
686static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
687 struct snd_ctl_elem_value *ucontrol)
688{
689 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
690 long *valp = ucontrol->value.integer.value;
691 int change;
692
693 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
694 HDA_AMP_MUTE,
695 valp[0] ? 0 : HDA_AMP_MUTE);
696 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
697 HDA_AMP_MUTE,
698 valp[1] ? 0 : HDA_AMP_MUTE);
699 if (change)
700 ad1986a_update_hp(codec);
701 return change;
702}
703
704static struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = {
705 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
706 {
707 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
708 .name = "Master Playback Switch",
709 .info = snd_hda_mixer_amp_switch_info,
710 .get = snd_hda_mixer_amp_switch_get,
711 .put = ad1986a_hp_master_sw_put,
712 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
713 },
714 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
715 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
716 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT),
717 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT),
718 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
719 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
720 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
721 HDA_CODEC_VOLUME("Beep Playback Volume", 0x18, 0x0, HDA_OUTPUT),
722 HDA_CODEC_MUTE("Beep Playback Switch", 0x18, 0x0, HDA_OUTPUT),
723 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
724 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
725 {
726 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
727 .name = "Capture Source",
728 .info = ad198x_mux_enum_info,
729 .get = ad198x_mux_enum_get,
730 .put = ad198x_mux_enum_put,
731 },
732 {
733 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
734 .name = "External Amplifier",
735 .info = ad198x_eapd_info,
736 .get = ad198x_eapd_get,
737 .put = ad198x_eapd_put,
738 .private_value = 0x1b | (1 << 8), /* port-D, inversed */
739 },
740 { } /* end */
741};
742
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743/*
744 * initialization verbs
745 */
746static struct hda_verb ad1986a_init_verbs[] = {
747 /* Front, Surround, CLFE DAC; mute as default */
748 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
749 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
750 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
751 /* Downmix - off */
752 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
753 /* HP, Line-Out, Surround, CLFE selectors */
754 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
755 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
756 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
757 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
758 /* Mono selector */
759 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
760 /* Mic selector: Mic 1/2 pin */
761 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
762 /* Line-in selector: Line-in */
763 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
764 /* Mic 1/2 swap */
765 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
766 /* Record selector: mic */
767 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
768 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
769 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
770 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
771 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
772 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
773 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
774 /* PC beep */
775 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
776 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
777 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
778 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
779 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
780 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
781 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200782 /* HP Pin */
783 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
784 /* Front, Surround, CLFE Pins */
785 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
786 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
787 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
788 /* Mono Pin */
789 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
790 /* Mic Pin */
791 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
792 /* Line, Aux, CD, Beep-In Pin */
793 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
794 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
795 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
796 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
797 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 { } /* end */
799};
800
Takashi Iwai9230d212006-03-13 13:49:49 +0100801static struct hda_verb ad1986a_ch2_init[] = {
802 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200803 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
804 /* Line-in selectors */
805 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100806 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200807 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
808 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
809 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100810 { } /* end */
811};
812
813static struct hda_verb ad1986a_ch4_init[] = {
814 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200815 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
816 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100817 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200818 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
819 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100820 { } /* end */
821};
822
823static struct hda_verb ad1986a_ch6_init[] = {
824 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200825 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
826 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100827 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200828 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
829 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100830 { } /* end */
831};
832
833static struct hda_channel_mode ad1986a_modes[3] = {
834 { 2, ad1986a_ch2_init },
835 { 4, ad1986a_ch4_init },
836 { 6, ad1986a_ch6_init },
837};
838
Takashi Iwai825aa972006-03-17 10:50:49 +0100839/* eapd initialization */
840static struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +0100841 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa972006-03-17 10:50:49 +0100842 {}
843};
844
Tobin Davisf36090f2007-01-08 11:07:12 +0100845/* Ultra initialization */
846static struct hda_verb ad1986a_ultra_init[] = {
847 /* eapd initialization */
848 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
849 /* CLFE -> Mic in */
850 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
851 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
852 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
853 { } /* end */
854};
855
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200856/* pin sensing on HP jack */
857static struct hda_verb ad1986a_hp_init_verbs[] = {
858 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
859 {}
860};
861
862
Takashi Iwai9230d212006-03-13 13:49:49 +0100863/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100864enum {
865 AD1986A_6STACK,
866 AD1986A_3STACK,
867 AD1986A_LAPTOP,
868 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200869 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +0100870 AD1986A_ULTRA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100871 AD1986A_MODELS
872};
Takashi Iwai9230d212006-03-13 13:49:49 +0100873
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100874static const char *ad1986a_models[AD1986A_MODELS] = {
875 [AD1986A_6STACK] = "6stack",
876 [AD1986A_3STACK] = "3stack",
877 [AD1986A_LAPTOP] = "laptop",
878 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200879 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +0100880 [AD1986A_ULTRA] = "ultra",
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100881};
882
883static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
884 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100885 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100886 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +0100887 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100888 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
889 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
890 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
891 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +0100892 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +0200893 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100894 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
895 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
896 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
897 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
898 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +0100899 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Takashi Iwai7db756f2007-12-24 14:36:09 +0100900 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
Tobin Davis18768992007-03-12 22:20:51 +0100901 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100902 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
903 SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),
904 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),
905 SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),
Tobin Davisf36090f2007-01-08 11:07:12 +0100906 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaiac3e3742007-12-17 17:14:18 +0100907 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +0100908 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100909 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200910 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100911 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +0100912 {}
913};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914
Takashi Iwaicb53c622007-08-10 17:21:45 +0200915#ifdef CONFIG_SND_HDA_POWER_SAVE
916static struct hda_amp_list ad1986a_loopbacks[] = {
917 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
918 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
919 { 0x15, HDA_OUTPUT, 0 }, /* CD */
920 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
921 { 0x17, HDA_OUTPUT, 0 }, /* Line */
922 { } /* end */
923};
924#endif
925
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926static int patch_ad1986a(struct hda_codec *codec)
927{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200928 struct ad198x_spec *spec;
Takashi Iwai9230d212006-03-13 13:49:49 +0100929 int board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
Takashi Iwaie560d8d2005-09-09 14:21:46 +0200931 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 if (spec == NULL)
933 return -ENOMEM;
934
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 codec->spec = spec;
936
937 spec->multiout.max_channels = 6;
938 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
939 spec->multiout.dac_nids = ad1986a_dac_nids;
940 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +0100941 spec->num_adc_nids = 1;
942 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +0100943 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200944 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +0100945 spec->num_mixers = 1;
946 spec->mixers[0] = ad1986a_mixers;
947 spec->num_init_verbs = 1;
948 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +0200949#ifdef CONFIG_SND_HDA_POWER_SAVE
950 spec->loopback.amplist = ad1986a_loopbacks;
951#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +0100952 spec->vmaster_nid = 0x1b;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200954 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955
Takashi Iwai9230d212006-03-13 13:49:49 +0100956 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100957 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
958 ad1986a_models,
959 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +0100960 switch (board_config) {
961 case AD1986A_3STACK:
962 spec->num_mixers = 2;
963 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +0200964 spec->num_init_verbs = 2;
965 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +0100966 spec->channel_mode = ad1986a_modes;
967 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +0200968 spec->need_dac_fix = 1;
969 spec->multiout.max_channels = 2;
970 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +0100971 break;
972 case AD1986A_LAPTOP:
973 spec->mixers[0] = ad1986a_laptop_mixers;
974 spec->multiout.max_channels = 2;
975 spec->multiout.num_dacs = 1;
976 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
977 break;
Takashi Iwai825aa972006-03-17 10:50:49 +0100978 case AD1986A_LAPTOP_EAPD:
979 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
980 spec->num_init_verbs = 2;
981 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
982 spec->multiout.max_channels = 2;
983 spec->multiout.num_dacs = 1;
984 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
985 spec->multiout.dig_out_nid = 0;
986 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
987 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200988 case AD1986A_LAPTOP_AUTOMUTE:
989 spec->mixers[0] = ad1986a_laptop_automute_mixers;
990 spec->num_init_verbs = 3;
991 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
992 spec->init_verbs[2] = ad1986a_hp_init_verbs;
993 spec->multiout.max_channels = 2;
994 spec->multiout.num_dacs = 1;
995 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
996 spec->multiout.dig_out_nid = 0;
997 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
998 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
999 codec->patch_ops.init = ad1986a_hp_init;
1000 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001001 case AD1986A_ULTRA:
1002 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1003 spec->num_init_verbs = 2;
1004 spec->init_verbs[1] = ad1986a_ultra_init;
1005 spec->multiout.max_channels = 2;
1006 spec->multiout.num_dacs = 1;
1007 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1008 spec->multiout.dig_out_nid = 0;
1009 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001010 }
1011
Takashi Iwaid29240c2007-10-26 12:35:56 +02001012 /* AD1986A has a hardware problem that it can't share a stream
1013 * with multiple output pins. The copy of front to surrounds
1014 * causes noisy or silent outputs at a certain timing, e.g.
1015 * changing the volume.
1016 * So, let's disable the shared stream.
1017 */
1018 spec->multiout.no_share_stream = 1;
1019
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 return 0;
1021}
1022
1023/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001024 * AD1983 specific
1025 */
1026
1027#define AD1983_SPDIF_OUT 0x02
1028#define AD1983_DAC 0x03
1029#define AD1983_ADC 0x04
1030
1031static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001032static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001033static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001034
1035static struct hda_input_mux ad1983_capture_source = {
1036 .num_items = 4,
1037 .items = {
1038 { "Mic", 0x0 },
1039 { "Line", 0x1 },
1040 { "Mix", 0x2 },
1041 { "Mix Mono", 0x3 },
1042 },
1043};
1044
1045/*
1046 * SPDIF playback route
1047 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001048static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001049{
1050 static char *texts[] = { "PCM", "ADC" };
1051
1052 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1053 uinfo->count = 1;
1054 uinfo->value.enumerated.items = 2;
1055 if (uinfo->value.enumerated.item > 1)
1056 uinfo->value.enumerated.item = 1;
1057 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1058 return 0;
1059}
1060
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001061static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001062{
1063 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1064 struct ad198x_spec *spec = codec->spec;
1065
1066 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1067 return 0;
1068}
1069
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001070static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001071{
1072 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1073 struct ad198x_spec *spec = codec->spec;
1074
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001075 if (ucontrol->value.enumerated.item[0] > 1)
1076 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001077 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1078 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001079 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1080 AC_VERB_SET_CONNECT_SEL,
1081 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001082 return 1;
1083 }
1084 return 0;
1085}
1086
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001087static struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001088 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1089 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1090 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1091 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1092 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1093 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1094 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1095 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1096 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1097 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1098 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1099 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1100 HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x10, 1, 0x0, HDA_OUTPUT),
1101 HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x10, 1, 0x0, HDA_OUTPUT),
1102 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
1103 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1104 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1105 {
1106 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1107 .name = "Capture Source",
1108 .info = ad198x_mux_enum_info,
1109 .get = ad198x_mux_enum_get,
1110 .put = ad198x_mux_enum_put,
1111 },
1112 {
1113 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001114 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001115 .info = ad1983_spdif_route_info,
1116 .get = ad1983_spdif_route_get,
1117 .put = ad1983_spdif_route_put,
1118 },
1119 { } /* end */
1120};
1121
1122static struct hda_verb ad1983_init_verbs[] = {
1123 /* Front, HP, Mono; mute as default */
1124 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1125 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1126 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1127 /* Beep, PCM, Mic, Line-In: mute */
1128 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1129 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1130 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1131 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1132 /* Front, HP selectors; from Mix */
1133 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1134 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1135 /* Mono selector; from Mix */
1136 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1137 /* Mic selector; Mic */
1138 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1139 /* Line-in selector: Line-in */
1140 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1141 /* Mic boost: 0dB */
1142 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1143 /* Record selector: mic */
1144 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1145 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1146 /* SPDIF route: PCM */
1147 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1148 /* Front Pin */
1149 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1150 /* HP Pin */
1151 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1152 /* Mono Pin */
1153 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1154 /* Mic Pin */
1155 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1156 /* Line Pin */
1157 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1158 { } /* end */
1159};
1160
Takashi Iwaicb53c622007-08-10 17:21:45 +02001161#ifdef CONFIG_SND_HDA_POWER_SAVE
1162static struct hda_amp_list ad1983_loopbacks[] = {
1163 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1164 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1165 { } /* end */
1166};
1167#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001168
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001169static int patch_ad1983(struct hda_codec *codec)
1170{
1171 struct ad198x_spec *spec;
1172
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001173 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001174 if (spec == NULL)
1175 return -ENOMEM;
1176
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001177 codec->spec = spec;
1178
1179 spec->multiout.max_channels = 2;
1180 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1181 spec->multiout.dac_nids = ad1983_dac_nids;
1182 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001183 spec->num_adc_nids = 1;
1184 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001185 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001186 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001187 spec->num_mixers = 1;
1188 spec->mixers[0] = ad1983_mixers;
1189 spec->num_init_verbs = 1;
1190 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001191 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001192#ifdef CONFIG_SND_HDA_POWER_SAVE
1193 spec->loopback.amplist = ad1983_loopbacks;
1194#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001195 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001196
1197 codec->patch_ops = ad198x_patch_ops;
1198
1199 return 0;
1200}
1201
1202
1203/*
1204 * AD1981 HD specific
1205 */
1206
1207#define AD1981_SPDIF_OUT 0x02
1208#define AD1981_DAC 0x03
1209#define AD1981_ADC 0x04
1210
1211static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001212static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001213static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001214
1215/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
1216static struct hda_input_mux ad1981_capture_source = {
1217 .num_items = 7,
1218 .items = {
1219 { "Front Mic", 0x0 },
1220 { "Line", 0x1 },
1221 { "Mix", 0x2 },
1222 { "Mix Mono", 0x3 },
1223 { "CD", 0x4 },
1224 { "Mic", 0x6 },
1225 { "Aux", 0x7 },
1226 },
1227};
1228
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001229static struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001230 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1231 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1232 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1233 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1234 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1235 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1236 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1237 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1238 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1239 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1240 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1241 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1242 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1243 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1244 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1245 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1246 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1247 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1248 HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
1249 HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x0d, 1, 0x0, HDA_OUTPUT),
1250 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
1251 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
1252 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1253 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1254 {
1255 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1256 .name = "Capture Source",
1257 .info = ad198x_mux_enum_info,
1258 .get = ad198x_mux_enum_get,
1259 .put = ad198x_mux_enum_put,
1260 },
1261 /* identical with AD1983 */
1262 {
1263 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001264 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001265 .info = ad1983_spdif_route_info,
1266 .get = ad1983_spdif_route_get,
1267 .put = ad1983_spdif_route_put,
1268 },
1269 { } /* end */
1270};
1271
1272static struct hda_verb ad1981_init_verbs[] = {
1273 /* Front, HP, Mono; mute as default */
1274 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1275 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1276 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1277 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1278 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1279 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1280 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1281 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1282 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1283 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1284 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1285 /* Front, HP selectors; from Mix */
1286 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1287 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1288 /* Mono selector; from Mix */
1289 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1290 /* Mic Mixer; select Front Mic */
1291 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1292 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1293 /* Mic boost: 0dB */
1294 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1295 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1296 /* Record selector: Front mic */
1297 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1298 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1299 /* SPDIF route: PCM */
1300 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1301 /* Front Pin */
1302 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1303 /* HP Pin */
1304 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1305 /* Mono Pin */
1306 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1307 /* Front & Rear Mic Pins */
1308 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1309 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1310 /* Line Pin */
1311 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1312 /* Digital Beep */
1313 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1314 /* Line-Out as Input: disabled */
1315 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1316 { } /* end */
1317};
1318
Takashi Iwaicb53c622007-08-10 17:21:45 +02001319#ifdef CONFIG_SND_HDA_POWER_SAVE
1320static struct hda_amp_list ad1981_loopbacks[] = {
1321 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1322 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1323 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1324 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1325 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1326 { } /* end */
1327};
1328#endif
1329
Takashi Iwai18a815d2006-03-01 19:54:39 +01001330/*
1331 * Patch for HP nx6320
1332 *
Tobin Davis18768992007-03-12 22:20:51 +01001333 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001334 * speaker output enabled _and_ mute-LED off.
1335 */
1336
1337#define AD1981_HP_EVENT 0x37
1338#define AD1981_MIC_EVENT 0x38
1339
1340static struct hda_verb ad1981_hp_init_verbs[] = {
1341 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1342 /* pin sensing on HP and Mic jacks */
1343 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1344 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1345 {}
1346};
1347
1348/* turn on/off EAPD (+ mute HP) as a master switch */
1349static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1350 struct snd_ctl_elem_value *ucontrol)
1351{
1352 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1353 struct ad198x_spec *spec = codec->spec;
1354
1355 if (! ad198x_eapd_put(kcontrol, ucontrol))
1356 return 0;
1357
1358 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001359 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1360 HDA_AMP_MUTE,
1361 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001362 return 1;
1363}
1364
1365/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02001366static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
1367 .ops = &snd_hda_bind_vol,
1368 .values = {
1369 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1370 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1371 0
1372 },
1373};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001374
1375/* mute internal speaker if HP is plugged */
1376static void ad1981_hp_automute(struct hda_codec *codec)
1377{
1378 unsigned int present;
1379
1380 present = snd_hda_codec_read(codec, 0x06, 0,
1381 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
Takashi Iwai47fd8302007-08-10 17:11:07 +02001382 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1383 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001384}
1385
1386/* toggle input of built-in and mic jack appropriately */
1387static void ad1981_hp_automic(struct hda_codec *codec)
1388{
1389 static struct hda_verb mic_jack_on[] = {
1390 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1391 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1392 {}
1393 };
1394 static struct hda_verb mic_jack_off[] = {
1395 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1396 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1397 {}
1398 };
1399 unsigned int present;
1400
1401 present = snd_hda_codec_read(codec, 0x08, 0,
1402 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1403 if (present)
1404 snd_hda_sequence_write(codec, mic_jack_on);
1405 else
1406 snd_hda_sequence_write(codec, mic_jack_off);
1407}
1408
1409/* unsolicited event for HP jack sensing */
1410static void ad1981_hp_unsol_event(struct hda_codec *codec,
1411 unsigned int res)
1412{
1413 res >>= 26;
1414 switch (res) {
1415 case AD1981_HP_EVENT:
1416 ad1981_hp_automute(codec);
1417 break;
1418 case AD1981_MIC_EVENT:
1419 ad1981_hp_automic(codec);
1420 break;
1421 }
1422}
1423
1424static struct hda_input_mux ad1981_hp_capture_source = {
1425 .num_items = 3,
1426 .items = {
1427 { "Mic", 0x0 },
1428 { "Docking-Station", 0x1 },
1429 { "Mix", 0x2 },
1430 },
1431};
1432
1433static struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001434 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001435 {
1436 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1437 .name = "Master Playback Switch",
1438 .info = ad198x_eapd_info,
1439 .get = ad198x_eapd_get,
1440 .put = ad1981_hp_master_sw_put,
1441 .private_value = 0x05,
1442 },
1443 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1444 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1445#if 0
1446 /* FIXME: analog mic/line loopback doesn't work with my tests...
1447 * (although recording is OK)
1448 */
1449 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1450 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1451 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1452 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1453 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1454 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1455 /* FIXME: does this laptop have analog CD connection? */
1456 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1457 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1458#endif
1459 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1460 HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
1461 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1462 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1463 {
1464 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1465 .name = "Capture Source",
1466 .info = ad198x_mux_enum_info,
1467 .get = ad198x_mux_enum_get,
1468 .put = ad198x_mux_enum_put,
1469 },
1470 { } /* end */
1471};
1472
1473/* initialize jack-sensing, too */
1474static int ad1981_hp_init(struct hda_codec *codec)
1475{
1476 ad198x_init(codec);
1477 ad1981_hp_automute(codec);
1478 ad1981_hp_automic(codec);
1479 return 0;
1480}
1481
Tobin Davis18768992007-03-12 22:20:51 +01001482/* configuration for Toshiba Laptops */
1483static struct hda_verb ad1981_toshiba_init_verbs[] = {
1484 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1485 /* pin sensing on HP and Mic jacks */
1486 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1487 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1488 {}
1489};
1490
1491static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
1492 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1493 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1494 { }
1495};
1496
Takashi Iwai01686c52006-04-18 12:54:11 +02001497/* configuration for Lenovo Thinkpad T60 */
1498static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
1499 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1500 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1501 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1502 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1503 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1504 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1505 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1506 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1507 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1508 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1509 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1510 {
1511 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1512 .name = "Capture Source",
1513 .info = ad198x_mux_enum_info,
1514 .get = ad198x_mux_enum_get,
1515 .put = ad198x_mux_enum_put,
1516 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001517 /* identical with AD1983 */
1518 {
1519 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1520 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1521 .info = ad1983_spdif_route_info,
1522 .get = ad1983_spdif_route_get,
1523 .put = ad1983_spdif_route_put,
1524 },
Takashi Iwai01686c52006-04-18 12:54:11 +02001525 { } /* end */
1526};
1527
1528static struct hda_input_mux ad1981_thinkpad_capture_source = {
1529 .num_items = 3,
1530 .items = {
1531 { "Mic", 0x0 },
1532 { "Mix", 0x2 },
1533 { "CD", 0x4 },
1534 },
1535};
1536
Takashi Iwai18a815d2006-03-01 19:54:39 +01001537/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001538enum {
1539 AD1981_BASIC,
1540 AD1981_HP,
1541 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001542 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001543 AD1981_MODELS
1544};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001545
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001546static const char *ad1981_models[AD1981_MODELS] = {
1547 [AD1981_HP] = "hp",
1548 [AD1981_THINKPAD] = "thinkpad",
1549 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001550 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001551};
1552
1553static struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001554 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001555 /* All HP models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001556 SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001557 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c52006-04-18 12:54:11 +02001558 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001559 SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001560 /* HP nx6320 (reversed SSID, H/W bug) */
1561 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001562 {}
1563};
1564
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001565static int patch_ad1981(struct hda_codec *codec)
1566{
1567 struct ad198x_spec *spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001568 int board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001569
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001570 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001571 if (spec == NULL)
1572 return -ENOMEM;
1573
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001574 codec->spec = spec;
1575
1576 spec->multiout.max_channels = 2;
1577 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1578 spec->multiout.dac_nids = ad1981_dac_nids;
1579 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001580 spec->num_adc_nids = 1;
1581 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001582 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001583 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001584 spec->num_mixers = 1;
1585 spec->mixers[0] = ad1981_mixers;
1586 spec->num_init_verbs = 1;
1587 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001588 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001589#ifdef CONFIG_SND_HDA_POWER_SAVE
1590 spec->loopback.amplist = ad1981_loopbacks;
1591#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001592 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001593
1594 codec->patch_ops = ad198x_patch_ops;
1595
Takashi Iwai18a815d2006-03-01 19:54:39 +01001596 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001597 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1598 ad1981_models,
1599 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001600 switch (board_config) {
1601 case AD1981_HP:
1602 spec->mixers[0] = ad1981_hp_mixers;
1603 spec->num_init_verbs = 2;
1604 spec->init_verbs[1] = ad1981_hp_init_verbs;
1605 spec->multiout.dig_out_nid = 0;
1606 spec->input_mux = &ad1981_hp_capture_source;
1607
1608 codec->patch_ops.init = ad1981_hp_init;
1609 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1610 break;
Takashi Iwai01686c52006-04-18 12:54:11 +02001611 case AD1981_THINKPAD:
1612 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c52006-04-18 12:54:11 +02001613 spec->input_mux = &ad1981_thinkpad_capture_source;
1614 break;
Tobin Davis18768992007-03-12 22:20:51 +01001615 case AD1981_TOSHIBA:
1616 spec->mixers[0] = ad1981_hp_mixers;
1617 spec->mixers[1] = ad1981_toshiba_mixers;
1618 spec->num_init_verbs = 2;
1619 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1620 spec->multiout.dig_out_nid = 0;
1621 spec->input_mux = &ad1981_hp_capture_source;
1622 codec->patch_ops.init = ad1981_hp_init;
1623 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1624 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001625 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001626 return 0;
1627}
1628
1629
1630/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001631 * AD1988
1632 *
1633 * Output pins and routes
1634 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001635 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001636 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1637 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1638 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1639 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1640 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1641 * port-F 0x16 (mute) <- 0x2a <- 06
1642 * port-G 0x24 (mute) <- 0x27 <- 05
1643 * port-H 0x25 (mute) <- 0x28 <- 0a
1644 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1645 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001646 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1647 * (*) 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 +01001648 *
1649 * Input pins and routes
1650 *
1651 * pin boost mix input # / adc input #
1652 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1653 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1654 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1655 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1656 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1657 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1658 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1659 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1660 *
1661 *
1662 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001663 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001664 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001665 *
1666 * Inputs of Analog Mix (0x20)
1667 * 0:Port-B (front mic)
1668 * 1:Port-C/G/H (line-in)
1669 * 2:Port-A
1670 * 3:Port-D (line-in/2)
1671 * 4:Port-E/G/H (mic-in)
1672 * 5:Port-F (mic2-in)
1673 * 6:CD
1674 * 7:Beep
1675 *
1676 * ADC selection
1677 * 0:Port-A
1678 * 1:Port-B (front mic-in)
1679 * 2:Port-C (line-in)
1680 * 3:Port-F (mic2-in)
1681 * 4:Port-E (mic-in)
1682 * 5:CD
1683 * 6:Port-G
1684 * 7:Port-H
1685 * 8:Port-D (line-in/2)
1686 * 9:Mix
1687 *
1688 * Proposed pin assignments by the datasheet
1689 *
1690 * 6-stack
1691 * Port-A front headphone
1692 * B front mic-in
1693 * C rear line-in
1694 * D rear front-out
1695 * E rear mic-in
1696 * F rear surround
1697 * G rear CLFE
1698 * H rear side
1699 *
1700 * 3-stack
1701 * Port-A front headphone
1702 * B front mic
1703 * C rear line-in/surround
1704 * D rear front-out
1705 * E rear mic-in/CLFE
1706 *
1707 * laptop
1708 * Port-A headphone
1709 * B mic-in
1710 * C docking station
1711 * D internal speaker (with EAPD)
1712 * E/F quad mic array
1713 */
1714
1715
1716/* models */
1717enum {
1718 AD1988_6STACK,
1719 AD1988_6STACK_DIG,
1720 AD1988_3STACK,
1721 AD1988_3STACK_DIG,
1722 AD1988_LAPTOP,
1723 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01001724 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001725 AD1988_MODEL_LAST,
1726};
1727
Takashi Iwaid32410b12005-11-24 16:06:23 +01001728/* reivision id to check workarounds */
1729#define AD1988A_REV2 0x100200
1730
Takashi Iwai1a806f42006-07-03 15:58:16 +02001731#define is_rev2(codec) \
1732 ((codec)->vendor_id == 0x11d41988 && \
1733 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001734
1735/*
1736 * mixers
1737 */
1738
Takashi Iwaid32410b12005-11-24 16:06:23 +01001739static hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001740 0x04, 0x06, 0x05, 0x0a
1741};
1742
Takashi Iwaid32410b12005-11-24 16:06:23 +01001743static hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001744 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01001745};
1746
1747/* for AD1988A revision-2, DAC2-4 are swapped */
1748static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
1749 0x04, 0x05, 0x0a, 0x06
1750};
1751
1752static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001753 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01001754};
1755
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001756static hda_nid_t ad1988_adc_nids[3] = {
1757 0x08, 0x09, 0x0f
1758};
1759
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001760static hda_nid_t ad1988_capsrc_nids[3] = {
1761 0x0c, 0x0d, 0x0e
1762};
1763
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001764#define AD1988_SPDIF_OUT 0x02
1765#define AD1988_SPDIF_IN 0x07
1766
1767static struct hda_input_mux ad1988_6stack_capture_source = {
1768 .num_items = 5,
1769 .items = {
1770 { "Front Mic", 0x0 },
1771 { "Line", 0x1 },
1772 { "Mic", 0x4 },
1773 { "CD", 0x5 },
1774 { "Mix", 0x9 },
1775 },
1776};
1777
1778static struct hda_input_mux ad1988_laptop_capture_source = {
1779 .num_items = 3,
1780 .items = {
1781 { "Mic/Line", 0x0 },
1782 { "CD", 0x5 },
1783 { "Mix", 0x9 },
1784 },
1785};
1786
1787/*
1788 */
1789static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
1790 struct snd_ctl_elem_info *uinfo)
1791{
1792 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1793 struct ad198x_spec *spec = codec->spec;
1794 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
1795 spec->num_channel_mode);
1796}
1797
1798static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
1799 struct snd_ctl_elem_value *ucontrol)
1800{
1801 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1802 struct ad198x_spec *spec = codec->spec;
1803 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
1804 spec->num_channel_mode, spec->multiout.max_channels);
1805}
1806
1807static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
1808 struct snd_ctl_elem_value *ucontrol)
1809{
1810 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1811 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001812 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
1813 spec->num_channel_mode,
1814 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02001815 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02001816 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001817 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001818}
1819
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001820/* 6-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001821static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001822 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1823 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1824 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
1825 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
1826 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001827 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001828};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001829
Takashi Iwaid32410b12005-11-24 16:06:23 +01001830static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
1831 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1832 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1833 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
1834 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
1835 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001836 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001837};
1838
1839static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001840 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
1841 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
1842 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
1843 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
1844 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
1845 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
1846 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
1847
1848 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
1849 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
1850 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
1851 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
1852 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
1853 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
1854 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
1855 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
1856
1857 HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
1858 HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
1859
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001860 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001861 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
1862
1863 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
1864 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
1865
1866 { } /* end */
1867};
1868
1869/* 3-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001870static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001871 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01001872 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001873 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
1874 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001875 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001876};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001877
Takashi Iwaid32410b12005-11-24 16:06:23 +01001878static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
1879 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001880 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
1881 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
1882 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001883 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001884};
1885
1886static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001887 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01001888 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
1889 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
1890 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001891 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
1892 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
1893
1894 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
1895 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
1896 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
1897 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
1898 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
1899 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
1900 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
1901 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
1902
1903 HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
1904 HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
1905
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001906 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001907 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
1908
1909 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
1910 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
1911 {
1912 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1913 .name = "Channel Mode",
1914 .info = ad198x_ch_mode_info,
1915 .get = ad198x_ch_mode_get,
1916 .put = ad198x_ch_mode_put,
1917 },
1918
1919 { } /* end */
1920};
1921
1922/* laptop mode */
1923static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
1924 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1925 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
1926 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
1927
1928 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
1929 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
1930 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
1931 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
1932 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
1933 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
1934
1935 HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
1936 HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
1937
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001938 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001939 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
1940
1941 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
1942
1943 {
1944 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1945 .name = "External Amplifier",
Takashi Iwai18a815d2006-03-01 19:54:39 +01001946 .info = ad198x_eapd_info,
1947 .get = ad198x_eapd_get,
1948 .put = ad198x_eapd_put,
1949 .private_value = 0x12 | (1 << 8), /* port-D, inversed */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001950 },
1951
1952 { } /* end */
1953};
1954
1955/* capture */
1956static struct snd_kcontrol_new ad1988_capture_mixers[] = {
1957 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
1958 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
1959 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
1960 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
1961 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
1962 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
1963 {
1964 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1965 /* The multiple "Capture Source" controls confuse alsamixer
1966 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001967 */
1968 /* .name = "Capture Source", */
1969 .name = "Input Source",
1970 .count = 3,
1971 .info = ad198x_mux_enum_info,
1972 .get = ad198x_mux_enum_get,
1973 .put = ad198x_mux_enum_put,
1974 },
1975 { } /* end */
1976};
1977
1978static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
1979 struct snd_ctl_elem_info *uinfo)
1980{
1981 static char *texts[] = {
1982 "PCM", "ADC1", "ADC2", "ADC3"
1983 };
1984 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1985 uinfo->count = 1;
1986 uinfo->value.enumerated.items = 4;
1987 if (uinfo->value.enumerated.item >= 4)
1988 uinfo->value.enumerated.item = 3;
1989 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1990 return 0;
1991}
1992
1993static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
1994 struct snd_ctl_elem_value *ucontrol)
1995{
1996 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1997 unsigned int sel;
1998
Takashi Iwaibddcf542007-07-24 18:04:05 +02001999 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2000 AC_AMP_GET_INPUT);
2001 if (!(sel & 0x80))
2002 ucontrol->value.enumerated.item[0] = 0;
2003 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002004 sel = snd_hda_codec_read(codec, 0x0b, 0,
2005 AC_VERB_GET_CONNECT_SEL, 0);
2006 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002007 sel++;
2008 else
2009 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002010 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002011 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002012 return 0;
2013}
2014
2015static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2016 struct snd_ctl_elem_value *ucontrol)
2017{
2018 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002019 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002020 int change;
2021
Takashi Iwai35b26722007-05-05 12:17:17 +02002022 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002023 if (val > 3)
2024 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002025 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002026 sel = snd_hda_codec_read(codec, 0x1d, 0,
2027 AC_VERB_GET_AMP_GAIN_MUTE,
2028 AC_AMP_GET_INPUT);
2029 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002030 if (change) {
2031 snd_hda_codec_write_cache(codec, 0x1d, 0,
2032 AC_VERB_SET_AMP_GAIN_MUTE,
2033 AMP_IN_UNMUTE(0));
2034 snd_hda_codec_write_cache(codec, 0x1d, 0,
2035 AC_VERB_SET_AMP_GAIN_MUTE,
2036 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002037 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002038 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002039 sel = snd_hda_codec_read(codec, 0x1d, 0,
2040 AC_VERB_GET_AMP_GAIN_MUTE,
2041 AC_AMP_GET_INPUT | 0x01);
2042 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002043 if (change) {
2044 snd_hda_codec_write_cache(codec, 0x1d, 0,
2045 AC_VERB_SET_AMP_GAIN_MUTE,
2046 AMP_IN_MUTE(0));
2047 snd_hda_codec_write_cache(codec, 0x1d, 0,
2048 AC_VERB_SET_AMP_GAIN_MUTE,
2049 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002050 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002051 sel = snd_hda_codec_read(codec, 0x0b, 0,
2052 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2053 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002054 if (change)
2055 snd_hda_codec_write_cache(codec, 0x0b, 0,
2056 AC_VERB_SET_CONNECT_SEL,
2057 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002058 }
2059 return change;
2060}
2061
2062static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
2063 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2064 {
2065 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2066 .name = "IEC958 Playback Source",
2067 .info = ad1988_spdif_playback_source_info,
2068 .get = ad1988_spdif_playback_source_get,
2069 .put = ad1988_spdif_playback_source_put,
2070 },
2071 { } /* end */
2072};
2073
2074static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
2075 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2076 { } /* end */
2077};
2078
2079
2080/*
2081 * initialization verbs
2082 */
2083
2084/*
2085 * for 6-stack (+dig)
2086 */
2087static struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002088 /* Front, Surround, CLFE, side DAC; unmute as default */
2089 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2090 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2091 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2092 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002093 /* Port-A front headphon path */
2094 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2095 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2096 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2097 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2098 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2099 /* Port-D line-out path */
2100 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2101 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2102 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2103 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2104 /* Port-F surround path */
2105 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2106 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2107 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2108 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2109 /* Port-G CLFE path */
2110 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2111 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2112 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2113 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2114 /* Port-H side path */
2115 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2116 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2117 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2118 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2119 /* Mono out path */
2120 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2121 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2122 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2123 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2124 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2125 /* Port-B front mic-in path */
2126 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2127 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2128 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2129 /* Port-C line-in path */
2130 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2131 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2132 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2133 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2134 /* Port-E mic-in path */
2135 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2136 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2137 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2138 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002139 /* Analog CD Input */
2140 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002141
2142 { }
2143};
2144
2145static struct hda_verb ad1988_capture_init_verbs[] = {
2146 /* mute analog mix */
2147 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2148 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2149 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2150 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2151 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2152 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2153 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2154 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2155 /* select ADCs - front-mic */
2156 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2157 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2158 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
2159 /* ADCs; muted */
2160 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2161 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2162 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2163
2164 { }
2165};
2166
2167static struct hda_verb ad1988_spdif_init_verbs[] = {
2168 /* SPDIF out sel */
2169 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2170 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2171 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002172 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002173 /* SPDIF out pin */
2174 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002175
2176 { }
2177};
2178
2179/*
2180 * verbs for 3stack (+dig)
2181 */
2182static struct hda_verb ad1988_3stack_ch2_init[] = {
2183 /* set port-C to line-in */
2184 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2185 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2186 /* set port-E to mic-in */
2187 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2188 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2189 { } /* end */
2190};
2191
2192static struct hda_verb ad1988_3stack_ch6_init[] = {
2193 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002194 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002195 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002196 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002197 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002198 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002199 { } /* end */
2200};
2201
2202static struct hda_channel_mode ad1988_3stack_modes[2] = {
2203 { 2, ad1988_3stack_ch2_init },
2204 { 6, ad1988_3stack_ch6_init },
2205};
2206
2207static struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002208 /* Front, Surround, CLFE, side DAC; unmute as default */
2209 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2210 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2211 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2212 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002213 /* Port-A front headphon path */
2214 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2215 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2216 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2217 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2218 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2219 /* Port-D line-out path */
2220 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2221 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2222 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2223 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2224 /* Mono out path */
2225 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2226 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2227 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2228 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2229 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2230 /* Port-B front mic-in path */
2231 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2232 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2233 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002234 /* Port-C line-in/surround path - 6ch mode as default */
2235 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2236 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002237 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002238 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002239 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002240 /* Port-E mic-in/CLFE path - 6ch mode as default */
2241 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2242 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002243 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002244 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002245 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2246 /* mute analog mix */
2247 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2248 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2249 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2250 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2251 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2252 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2253 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2254 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2255 /* select ADCs - front-mic */
2256 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2257 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2258 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
2259 /* ADCs; muted */
2260 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2261 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2262 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2263 { }
2264};
2265
2266/*
2267 * verbs for laptop mode (+dig)
2268 */
2269static struct hda_verb ad1988_laptop_hp_on[] = {
2270 /* unmute port-A and mute port-D */
2271 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2272 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2273 { } /* end */
2274};
2275static struct hda_verb ad1988_laptop_hp_off[] = {
2276 /* mute port-A and unmute port-D */
2277 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2278 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2279 { } /* end */
2280};
2281
2282#define AD1988_HP_EVENT 0x01
2283
2284static struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002285 /* Front, Surround, CLFE, side DAC; unmute as default */
2286 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2287 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2288 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2289 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002290 /* Port-A front headphon path */
2291 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2292 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2293 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2294 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2295 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2296 /* unsolicited event for pin-sense */
2297 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2298 /* Port-D line-out path + EAPD */
2299 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2300 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2301 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2302 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2303 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2304 /* Mono out path */
2305 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2306 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2307 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2308 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2309 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2310 /* Port-B mic-in path */
2311 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2312 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2313 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2314 /* Port-C docking station - try to output */
2315 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2316 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2317 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2318 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2319 /* mute analog mix */
2320 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2321 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2322 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2323 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2324 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2325 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2326 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2327 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2328 /* select ADCs - mic */
2329 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2330 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2331 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
2332 /* ADCs; muted */
2333 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2334 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2335 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2336 { }
2337};
2338
2339static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2340{
2341 if ((res >> 26) != AD1988_HP_EVENT)
2342 return;
2343 if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31))
2344 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2345 else
2346 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2347}
2348
Takashi Iwaicb53c622007-08-10 17:21:45 +02002349#ifdef CONFIG_SND_HDA_POWER_SAVE
2350static struct hda_amp_list ad1988_loopbacks[] = {
2351 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2352 { 0x20, HDA_INPUT, 1 }, /* Line */
2353 { 0x20, HDA_INPUT, 4 }, /* Mic */
2354 { 0x20, HDA_INPUT, 6 }, /* CD */
2355 { } /* end */
2356};
2357#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002358
2359/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002360 * Automatic parse of I/O pins from the BIOS configuration
2361 */
2362
2363#define NUM_CONTROL_ALLOC 32
2364#define NUM_VERB_ALLOC 32
2365
2366enum {
2367 AD_CTL_WIDGET_VOL,
2368 AD_CTL_WIDGET_MUTE,
2369 AD_CTL_BIND_MUTE,
2370};
2371static struct snd_kcontrol_new ad1988_control_templates[] = {
2372 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2373 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2374 HDA_BIND_MUTE(NULL, 0, 0, 0),
2375};
2376
2377/* add dynamic controls */
2378static int add_control(struct ad198x_spec *spec, int type, const char *name,
2379 unsigned long val)
2380{
2381 struct snd_kcontrol_new *knew;
2382
2383 if (spec->num_kctl_used >= spec->num_kctl_alloc) {
2384 int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
2385
2386 knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
2387 if (! knew)
2388 return -ENOMEM;
2389 if (spec->kctl_alloc) {
2390 memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
2391 kfree(spec->kctl_alloc);
2392 }
2393 spec->kctl_alloc = knew;
2394 spec->num_kctl_alloc = num;
2395 }
2396
2397 knew = &spec->kctl_alloc[spec->num_kctl_used];
2398 *knew = ad1988_control_templates[type];
2399 knew->name = kstrdup(name, GFP_KERNEL);
2400 if (! knew->name)
2401 return -ENOMEM;
2402 knew->private_value = val;
2403 spec->num_kctl_used++;
2404 return 0;
2405}
2406
2407#define AD1988_PIN_CD_NID 0x18
2408#define AD1988_PIN_BEEP_NID 0x10
2409
2410static hda_nid_t ad1988_mixer_nids[8] = {
2411 /* A B C D E F G H */
2412 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2413};
2414
2415static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2416{
2417 static hda_nid_t idx_to_dac[8] = {
2418 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002419 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002420 };
2421 static hda_nid_t idx_to_dac_rev2[8] = {
2422 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002423 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002424 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002425 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002426 return idx_to_dac_rev2[idx];
2427 else
2428 return idx_to_dac[idx];
2429}
2430
2431static hda_nid_t ad1988_boost_nids[8] = {
2432 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2433};
2434
2435static int ad1988_pin_idx(hda_nid_t nid)
2436{
2437 static hda_nid_t ad1988_io_pins[8] = {
2438 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2439 };
2440 int i;
2441 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2442 if (ad1988_io_pins[i] == nid)
2443 return i;
2444 return 0; /* should be -1 */
2445}
2446
2447static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2448{
2449 static int loopback_idx[8] = {
2450 2, 0, 1, 3, 4, 5, 1, 4
2451 };
2452 switch (nid) {
2453 case AD1988_PIN_CD_NID:
2454 return 6;
2455 default:
2456 return loopback_idx[ad1988_pin_idx(nid)];
2457 }
2458}
2459
2460static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2461{
2462 static int adc_idx[8] = {
2463 0, 1, 2, 8, 4, 3, 6, 7
2464 };
2465 switch (nid) {
2466 case AD1988_PIN_CD_NID:
2467 return 5;
2468 default:
2469 return adc_idx[ad1988_pin_idx(nid)];
2470 }
2471}
2472
2473/* fill in the dac_nids table from the parsed pin configuration */
2474static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2475 const struct auto_pin_cfg *cfg)
2476{
2477 struct ad198x_spec *spec = codec->spec;
2478 int i, idx;
2479
2480 spec->multiout.dac_nids = spec->private_dac_nids;
2481
2482 /* check the pins hardwired to audio widget */
2483 for (i = 0; i < cfg->line_outs; i++) {
2484 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2485 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2486 }
2487 spec->multiout.num_dacs = cfg->line_outs;
2488 return 0;
2489}
2490
2491/* add playback controls from the parsed DAC table */
2492static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2493 const struct auto_pin_cfg *cfg)
2494{
2495 char name[32];
2496 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2497 hda_nid_t nid;
2498 int i, err;
2499
2500 for (i = 0; i < cfg->line_outs; i++) {
2501 hda_nid_t dac = spec->multiout.dac_nids[i];
2502 if (! dac)
2503 continue;
2504 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2505 if (i == 2) {
2506 /* Center/LFE */
2507 err = add_control(spec, AD_CTL_WIDGET_VOL,
2508 "Center Playback Volume",
2509 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2510 if (err < 0)
2511 return err;
2512 err = add_control(spec, AD_CTL_WIDGET_VOL,
2513 "LFE Playback Volume",
2514 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2515 if (err < 0)
2516 return err;
2517 err = add_control(spec, AD_CTL_BIND_MUTE,
2518 "Center Playback Switch",
2519 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2520 if (err < 0)
2521 return err;
2522 err = add_control(spec, AD_CTL_BIND_MUTE,
2523 "LFE Playback Switch",
2524 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2525 if (err < 0)
2526 return err;
2527 } else {
2528 sprintf(name, "%s Playback Volume", chname[i]);
2529 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2530 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2531 if (err < 0)
2532 return err;
2533 sprintf(name, "%s Playback Switch", chname[i]);
2534 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2535 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2536 if (err < 0)
2537 return err;
2538 }
2539 }
2540 return 0;
2541}
2542
2543/* add playback controls for speaker and HP outputs */
2544static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2545 const char *pfx)
2546{
2547 struct ad198x_spec *spec = codec->spec;
2548 hda_nid_t nid;
2549 int idx, err;
2550 char name[32];
2551
2552 if (! pin)
2553 return 0;
2554
2555 idx = ad1988_pin_idx(pin);
2556 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai82bc9552006-03-21 11:24:42 +01002557 /* specify the DAC as the extra output */
2558 if (! spec->multiout.hp_nid)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002559 spec->multiout.hp_nid = nid;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002560 else
2561 spec->multiout.extra_out_nid[0] = nid;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002562 /* control HP volume/switch on the output mixer amp */
2563 sprintf(name, "%s Playback Volume", pfx);
2564 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2565 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
2566 return err;
2567 nid = ad1988_mixer_nids[idx];
2568 sprintf(name, "%s Playback Switch", pfx);
2569 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2570 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2571 return err;
2572 return 0;
2573}
2574
2575/* create input playback/capture controls for the given pin */
2576static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2577 const char *ctlname, int boost)
2578{
2579 char name[32];
2580 int err, idx;
2581
2582 sprintf(name, "%s Playback Volume", ctlname);
2583 idx = ad1988_pin_to_loopback_idx(pin);
2584 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2585 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2586 return err;
2587 sprintf(name, "%s Playback Switch", ctlname);
2588 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2589 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2590 return err;
2591 if (boost) {
2592 hda_nid_t bnid;
2593 idx = ad1988_pin_idx(pin);
2594 bnid = ad1988_boost_nids[idx];
2595 if (bnid) {
2596 sprintf(name, "%s Boost", ctlname);
2597 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2598 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2599
2600 }
2601 }
2602 return 0;
2603}
2604
2605/* create playback/capture controls for input pins */
2606static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2607 const struct auto_pin_cfg *cfg)
2608{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002609 struct hda_input_mux *imux = &spec->private_imux;
2610 int i, err;
2611
2612 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002613 err = new_analog_input(spec, cfg->input_pins[i],
2614 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002615 i <= AUTO_PIN_FRONT_MIC);
2616 if (err < 0)
2617 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002618 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002619 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2620 imux->num_items++;
2621 }
2622 imux->items[imux->num_items].label = "Mix";
2623 imux->items[imux->num_items].index = 9;
2624 imux->num_items++;
2625
2626 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2627 "Analog Mix Playback Volume",
2628 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2629 return err;
2630 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2631 "Analog Mix Playback Switch",
2632 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2633 return err;
2634
2635 return 0;
2636}
2637
2638static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2639 hda_nid_t nid, int pin_type,
2640 int dac_idx)
2641{
2642 /* set as output */
2643 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2644 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2645 switch (nid) {
2646 case 0x11: /* port-A - DAC 04 */
2647 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2648 break;
2649 case 0x14: /* port-B - DAC 06 */
2650 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2651 break;
2652 case 0x15: /* port-C - DAC 05 */
2653 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2654 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002655 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002656 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2657 break;
2658 case 0x13: /* mono - DAC 04 */
2659 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2660 break;
2661 }
2662}
2663
2664static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2665{
2666 struct ad198x_spec *spec = codec->spec;
2667 int i;
2668
2669 for (i = 0; i < spec->autocfg.line_outs; i++) {
2670 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2671 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2672 }
2673}
2674
2675static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2676{
2677 struct ad198x_spec *spec = codec->spec;
2678 hda_nid_t pin;
2679
Takashi Iwai82bc9552006-03-21 11:24:42 +01002680 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002681 if (pin) /* connect to front */
2682 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002683 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002684 if (pin) /* connect to front */
2685 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2686}
2687
2688static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2689{
2690 struct ad198x_spec *spec = codec->spec;
2691 int i, idx;
2692
2693 for (i = 0; i < AUTO_PIN_LAST; i++) {
2694 hda_nid_t nid = spec->autocfg.input_pins[i];
2695 if (! nid)
2696 continue;
2697 switch (nid) {
2698 case 0x15: /* port-C */
2699 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2700 break;
2701 case 0x17: /* port-E */
2702 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2703 break;
2704 }
2705 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2706 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2707 if (nid != AD1988_PIN_CD_NID)
2708 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
2709 AMP_OUT_MUTE);
2710 idx = ad1988_pin_idx(nid);
2711 if (ad1988_boost_nids[idx])
2712 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
2713 AC_VERB_SET_AMP_GAIN_MUTE,
2714 AMP_OUT_ZERO);
2715 }
2716}
2717
2718/* parse the BIOS configuration and set up the alc_spec */
2719/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
2720static int ad1988_parse_auto_config(struct hda_codec *codec)
2721{
2722 struct ad198x_spec *spec = codec->spec;
2723 int err;
2724
Kailang Yangdf694da2005-12-05 19:42:22 +01002725 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002726 return err;
2727 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
2728 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002729 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002730 return 0; /* can't find valid BIOS pin config */
2731 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01002732 (err = ad1988_auto_create_extra_out(codec,
2733 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002734 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002735 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002736 "Headphone")) < 0 ||
2737 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
2738 return err;
2739
2740 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2741
2742 if (spec->autocfg.dig_out_pin)
2743 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2744 if (spec->autocfg.dig_in_pin)
2745 spec->dig_in_nid = AD1988_SPDIF_IN;
2746
2747 if (spec->kctl_alloc)
2748 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
2749
2750 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
2751
2752 spec->input_mux = &spec->private_imux;
2753
2754 return 1;
2755}
2756
2757/* init callback for auto-configuration model -- overriding the default init */
2758static int ad1988_auto_init(struct hda_codec *codec)
2759{
2760 ad198x_init(codec);
2761 ad1988_auto_init_multi_out(codec);
2762 ad1988_auto_init_extra_out(codec);
2763 ad1988_auto_init_analog_input(codec);
2764 return 0;
2765}
2766
2767
2768/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002769 */
2770
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002771static const char *ad1988_models[AD1988_MODEL_LAST] = {
2772 [AD1988_6STACK] = "6stack",
2773 [AD1988_6STACK_DIG] = "6stack-dig",
2774 [AD1988_3STACK] = "3stack",
2775 [AD1988_3STACK_DIG] = "3stack-dig",
2776 [AD1988_LAPTOP] = "laptop",
2777 [AD1988_LAPTOP_DIG] = "laptop-dig",
2778 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002779};
2780
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002781static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002782 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002783 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002784 {}
2785};
2786
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002787static int patch_ad1988(struct hda_codec *codec)
2788{
2789 struct ad198x_spec *spec;
2790 int board_config;
2791
2792 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2793 if (spec == NULL)
2794 return -ENOMEM;
2795
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002796 codec->spec = spec;
2797
Takashi Iwai1a806f42006-07-03 15:58:16 +02002798 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002799 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
2800
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002801 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002802 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002803 if (board_config < 0) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002804 printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n");
2805 board_config = AD1988_AUTO;
2806 }
2807
2808 if (board_config == AD1988_AUTO) {
2809 /* automatic parse from the BIOS config */
2810 int err = ad1988_parse_auto_config(codec);
2811 if (err < 0) {
2812 ad198x_free(codec);
2813 return err;
2814 } else if (! err) {
2815 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
2816 board_config = AD1988_6STACK;
2817 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002818 }
2819
2820 switch (board_config) {
2821 case AD1988_6STACK:
2822 case AD1988_6STACK_DIG:
2823 spec->multiout.max_channels = 8;
2824 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002825 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002826 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
2827 else
2828 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002829 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002830 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002831 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002832 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
2833 else
2834 spec->mixers[0] = ad1988_6stack_mixers1;
2835 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002836 spec->num_init_verbs = 1;
2837 spec->init_verbs[0] = ad1988_6stack_init_verbs;
2838 if (board_config == AD1988_6STACK_DIG) {
2839 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2840 spec->dig_in_nid = AD1988_SPDIF_IN;
2841 }
2842 break;
2843 case AD1988_3STACK:
2844 case AD1988_3STACK_DIG:
2845 spec->multiout.max_channels = 6;
2846 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002847 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002848 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
2849 else
2850 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002851 spec->input_mux = &ad1988_6stack_capture_source;
2852 spec->channel_mode = ad1988_3stack_modes;
2853 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002854 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002855 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002856 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
2857 else
2858 spec->mixers[0] = ad1988_3stack_mixers1;
2859 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002860 spec->num_init_verbs = 1;
2861 spec->init_verbs[0] = ad1988_3stack_init_verbs;
2862 if (board_config == AD1988_3STACK_DIG)
2863 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2864 break;
2865 case AD1988_LAPTOP:
2866 case AD1988_LAPTOP_DIG:
2867 spec->multiout.max_channels = 2;
2868 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002869 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002870 spec->input_mux = &ad1988_laptop_capture_source;
2871 spec->num_mixers = 1;
2872 spec->mixers[0] = ad1988_laptop_mixers;
2873 spec->num_init_verbs = 1;
2874 spec->init_verbs[0] = ad1988_laptop_init_verbs;
2875 if (board_config == AD1988_LAPTOP_DIG)
2876 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2877 break;
2878 }
2879
Takashi Iwaid32410b12005-11-24 16:06:23 +01002880 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
2881 spec->adc_nids = ad1988_adc_nids;
2882 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002883 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
2884 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
2885 if (spec->multiout.dig_out_nid) {
2886 spec->mixers[spec->num_mixers++] = ad1988_spdif_out_mixers;
2887 spec->init_verbs[spec->num_init_verbs++] = ad1988_spdif_init_verbs;
2888 }
2889 if (spec->dig_in_nid)
2890 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
2891
2892 codec->patch_ops = ad198x_patch_ops;
2893 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002894 case AD1988_AUTO:
2895 codec->patch_ops.init = ad1988_auto_init;
2896 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002897 case AD1988_LAPTOP:
2898 case AD1988_LAPTOP_DIG:
2899 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
2900 break;
2901 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02002902#ifdef CONFIG_SND_HDA_POWER_SAVE
2903 spec->loopback.amplist = ad1988_loopbacks;
2904#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01002905 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002906
2907 return 0;
2908}
2909
2910
2911/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02002912 * AD1884 / AD1984
2913 *
2914 * port-B - front line/mic-in
2915 * port-E - aux in/out
2916 * port-F - aux in/out
2917 * port-C - rear line/mic-in
2918 * port-D - rear line/hp-out
2919 * port-A - front line/hp-out
2920 *
2921 * AD1984 = AD1884 + two digital mic-ins
2922 *
2923 * FIXME:
2924 * For simplicity, we share the single DAC for both HP and line-outs
2925 * right now. The inidividual playbacks could be easily implemented,
2926 * but no build-up framework is given, so far.
2927 */
2928
2929static hda_nid_t ad1884_dac_nids[1] = {
2930 0x04,
2931};
2932
2933static hda_nid_t ad1884_adc_nids[2] = {
2934 0x08, 0x09,
2935};
2936
2937static hda_nid_t ad1884_capsrc_nids[2] = {
2938 0x0c, 0x0d,
2939};
2940
2941#define AD1884_SPDIF_OUT 0x02
2942
2943static struct hda_input_mux ad1884_capture_source = {
2944 .num_items = 4,
2945 .items = {
2946 { "Front Mic", 0x0 },
2947 { "Mic", 0x1 },
2948 { "CD", 0x2 },
2949 { "Mix", 0x3 },
2950 },
2951};
2952
2953static struct snd_kcontrol_new ad1884_base_mixers[] = {
2954 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2955 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
2956 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
2957 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
2958 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
2959 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
2960 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
2961 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
2962 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
2963 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
2964 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
2965 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
2966 /*
2967 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
2968 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
2969 HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
2970 HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
2971 */
2972 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
2973 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
2974 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2975 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2976 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2977 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2978 {
2979 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2980 /* The multiple "Capture Source" controls confuse alsamixer
2981 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02002982 */
2983 /* .name = "Capture Source", */
2984 .name = "Input Source",
2985 .count = 2,
2986 .info = ad198x_mux_enum_info,
2987 .get = ad198x_mux_enum_get,
2988 .put = ad198x_mux_enum_put,
2989 },
2990 /* SPDIF controls */
2991 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2992 {
2993 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2994 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
2995 /* identical with ad1983 */
2996 .info = ad1983_spdif_route_info,
2997 .get = ad1983_spdif_route_get,
2998 .put = ad1983_spdif_route_put,
2999 },
3000 { } /* end */
3001};
3002
3003static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3004 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3005 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3006 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003007 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003008 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003009 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003010 { } /* end */
3011};
3012
3013/*
3014 * initialization verbs
3015 */
3016static struct hda_verb ad1884_init_verbs[] = {
3017 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003018 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3019 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003020 /* Port-A (HP) mixer */
3021 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3022 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3023 /* Port-A pin */
3024 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3025 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3026 /* HP selector - select DAC2 */
3027 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3028 /* Port-D (Line-out) mixer */
3029 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3030 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3031 /* Port-D pin */
3032 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3033 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3034 /* Mono-out mixer */
3035 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3036 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3037 /* Mono-out pin */
3038 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3039 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3040 /* Mono selector */
3041 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3042 /* Port-B (front mic) pin */
3043 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3044 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3045 /* Port-C (rear mic) pin */
3046 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3047 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3048 /* Analog mixer; mute as default */
3049 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3050 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3051 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3052 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3053 /* Analog Mix output amp */
3054 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3055 /* SPDIF output selector */
3056 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3057 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3058 { } /* end */
3059};
3060
Takashi Iwaicb53c622007-08-10 17:21:45 +02003061#ifdef CONFIG_SND_HDA_POWER_SAVE
3062static struct hda_amp_list ad1884_loopbacks[] = {
3063 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3064 { 0x20, HDA_INPUT, 1 }, /* Mic */
3065 { 0x20, HDA_INPUT, 2 }, /* CD */
3066 { 0x20, HDA_INPUT, 4 }, /* Docking */
3067 { } /* end */
3068};
3069#endif
3070
Takashi Iwai2134ea42008-01-10 16:53:55 +01003071static const char *ad1884_slave_vols[] = {
3072 "PCM Playback Volume",
3073 "Mic Playback Volume",
3074 "Mono Playback Volume",
3075 "Front Mic Playback Volume",
3076 "Mic Playback Volume",
3077 "CD Playback Volume",
3078 "Internal Mic Playback Volume",
3079 "Docking Mic Playback Volume"
3080 "Beep Playback Volume",
3081 NULL
3082};
3083
Takashi Iwai2bac6472007-05-18 18:21:41 +02003084static int patch_ad1884(struct hda_codec *codec)
3085{
3086 struct ad198x_spec *spec;
3087
3088 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3089 if (spec == NULL)
3090 return -ENOMEM;
3091
3092 mutex_init(&spec->amp_mutex);
3093 codec->spec = spec;
3094
3095 spec->multiout.max_channels = 2;
3096 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3097 spec->multiout.dac_nids = ad1884_dac_nids;
3098 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3099 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3100 spec->adc_nids = ad1884_adc_nids;
3101 spec->capsrc_nids = ad1884_capsrc_nids;
3102 spec->input_mux = &ad1884_capture_source;
3103 spec->num_mixers = 1;
3104 spec->mixers[0] = ad1884_base_mixers;
3105 spec->num_init_verbs = 1;
3106 spec->init_verbs[0] = ad1884_init_verbs;
3107 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003108#ifdef CONFIG_SND_HDA_POWER_SAVE
3109 spec->loopback.amplist = ad1884_loopbacks;
3110#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003111 spec->vmaster_nid = 0x04;
3112 /* we need to cover all playback volumes */
3113 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003114
3115 codec->patch_ops = ad198x_patch_ops;
3116
3117 return 0;
3118}
3119
3120/*
3121 * Lenovo Thinkpad T61/X61
3122 */
3123static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwai3b194402007-06-04 18:32:23 +02003124 .num_items = 3,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003125 .items = {
3126 { "Mic", 0x0 },
3127 { "Internal Mic", 0x1 },
3128 { "Mix", 0x3 },
3129 },
3130};
3131
3132static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3133 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3134 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3135 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3136 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3137 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3138 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3139 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3140 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3141 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3142 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003143 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3144 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003145 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003146 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3147 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003148 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3149 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3150 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3151 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3152 {
3153 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3154 /* The multiple "Capture Source" controls confuse alsamixer
3155 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003156 */
3157 /* .name = "Capture Source", */
3158 .name = "Input Source",
3159 .count = 2,
3160 .info = ad198x_mux_enum_info,
3161 .get = ad198x_mux_enum_get,
3162 .put = ad198x_mux_enum_put,
3163 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003164 /* SPDIF controls */
3165 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3166 {
3167 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3168 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3169 /* identical with ad1983 */
3170 .info = ad1983_spdif_route_info,
3171 .get = ad1983_spdif_route_get,
3172 .put = ad1983_spdif_route_put,
3173 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003174 { } /* end */
3175};
3176
3177/* additional verbs */
3178static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3179 /* Port-E (docking station mic) pin */
3180 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3181 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3182 /* docking mic boost */
3183 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3184 /* Analog mixer - docking mic; mute as default */
3185 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003186 /* enable EAPD bit */
3187 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003188 { } /* end */
3189};
3190
3191/* Digial MIC ADC NID 0x05 + 0x06 */
3192static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3193 struct hda_codec *codec,
3194 unsigned int stream_tag,
3195 unsigned int format,
3196 struct snd_pcm_substream *substream)
3197{
3198 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3199 stream_tag, 0, format);
3200 return 0;
3201}
3202
3203static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3204 struct hda_codec *codec,
3205 struct snd_pcm_substream *substream)
3206{
3207 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3208 0, 0, 0);
3209 return 0;
3210}
3211
3212static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3213 .substreams = 2,
3214 .channels_min = 2,
3215 .channels_max = 2,
3216 .nid = 0x05,
3217 .ops = {
3218 .prepare = ad1984_pcm_dmic_prepare,
3219 .cleanup = ad1984_pcm_dmic_cleanup
3220 },
3221};
3222
3223static int ad1984_build_pcms(struct hda_codec *codec)
3224{
3225 struct ad198x_spec *spec = codec->spec;
3226 struct hda_pcm *info;
3227 int err;
3228
3229 err = ad198x_build_pcms(codec);
3230 if (err < 0)
3231 return err;
3232
3233 info = spec->pcm_rec + codec->num_pcms;
3234 codec->num_pcms++;
3235 info->name = "AD1984 Digital Mic";
3236 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3237 return 0;
3238}
3239
3240/* models */
3241enum {
3242 AD1984_BASIC,
3243 AD1984_THINKPAD,
3244 AD1984_MODELS
3245};
3246
3247static const char *ad1984_models[AD1984_MODELS] = {
3248 [AD1984_BASIC] = "basic",
3249 [AD1984_THINKPAD] = "thinkpad",
3250};
3251
3252static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3253 /* Lenovo Thinkpad T61/X61 */
3254 SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
3255 {}
3256};
3257
3258static int patch_ad1984(struct hda_codec *codec)
3259{
3260 struct ad198x_spec *spec;
3261 int board_config, err;
3262
3263 err = patch_ad1884(codec);
3264 if (err < 0)
3265 return err;
3266 spec = codec->spec;
3267 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3268 ad1984_models, ad1984_cfg_tbl);
3269 switch (board_config) {
3270 case AD1984_BASIC:
3271 /* additional digital mics */
3272 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3273 codec->patch_ops.build_pcms = ad1984_build_pcms;
3274 break;
3275 case AD1984_THINKPAD:
Jerone Youngebf00c52008-01-07 12:22:18 +01003276 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003277 spec->input_mux = &ad1984_thinkpad_capture_source;
3278 spec->mixers[0] = ad1984_thinkpad_mixers;
3279 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
3280 break;
3281 }
3282 return 0;
3283}
3284
3285
3286/*
Takashi Iwai0ac85512007-06-20 15:46:13 +02003287 * AD1882
3288 *
3289 * port-A - front hp-out
3290 * port-B - front mic-in
3291 * port-C - rear line-in, shared surr-out (3stack)
3292 * port-D - rear line-out
3293 * port-E - rear mic-in, shared clfe-out (3stack)
3294 * port-F - rear surr-out (6stack)
3295 * port-G - rear clfe-out (6stack)
3296 */
3297
3298static hda_nid_t ad1882_dac_nids[3] = {
3299 0x04, 0x03, 0x05
3300};
3301
3302static hda_nid_t ad1882_adc_nids[2] = {
3303 0x08, 0x09,
3304};
3305
3306static hda_nid_t ad1882_capsrc_nids[2] = {
3307 0x0c, 0x0d,
3308};
3309
3310#define AD1882_SPDIF_OUT 0x02
3311
3312/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
3313static struct hda_input_mux ad1882_capture_source = {
3314 .num_items = 5,
3315 .items = {
3316 { "Front Mic", 0x1 },
3317 { "Mic", 0x4 },
3318 { "Line", 0x2 },
3319 { "CD", 0x3 },
3320 { "Mix", 0x7 },
3321 },
3322};
3323
3324static struct snd_kcontrol_new ad1882_base_mixers[] = {
3325 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3326 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
3327 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
3328 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
3329 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3330 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3331 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3332 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3333 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3334 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3335 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3336 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3337 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
3338 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
3339 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
3340 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
3341 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
3342 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
3343 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
3344 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
3345 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
3346 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3347 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3348 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3349 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3350 {
3351 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3352 /* The multiple "Capture Source" controls confuse alsamixer
3353 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02003354 */
3355 /* .name = "Capture Source", */
3356 .name = "Input Source",
3357 .count = 2,
3358 .info = ad198x_mux_enum_info,
3359 .get = ad198x_mux_enum_get,
3360 .put = ad198x_mux_enum_put,
3361 },
3362 /* SPDIF controls */
3363 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3364 {
3365 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3366 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3367 /* identical with ad1983 */
3368 .info = ad1983_spdif_route_info,
3369 .get = ad1983_spdif_route_get,
3370 .put = ad1983_spdif_route_put,
3371 },
3372 { } /* end */
3373};
3374
3375static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
3376 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
3377 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
3378 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
3379 {
3380 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3381 .name = "Channel Mode",
3382 .info = ad198x_ch_mode_info,
3383 .get = ad198x_ch_mode_get,
3384 .put = ad198x_ch_mode_put,
3385 },
3386 { } /* end */
3387};
3388
3389static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
3390 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
3391 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
3392 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
3393 { } /* end */
3394};
3395
3396static struct hda_verb ad1882_ch2_init[] = {
3397 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3398 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3399 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3400 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3401 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3402 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3403 { } /* end */
3404};
3405
3406static struct hda_verb ad1882_ch4_init[] = {
3407 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3408 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3409 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3410 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3411 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3412 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3413 { } /* end */
3414};
3415
3416static struct hda_verb ad1882_ch6_init[] = {
3417 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3418 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3419 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3420 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3421 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3422 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3423 { } /* end */
3424};
3425
3426static struct hda_channel_mode ad1882_modes[3] = {
3427 { 2, ad1882_ch2_init },
3428 { 4, ad1882_ch4_init },
3429 { 6, ad1882_ch6_init },
3430};
3431
3432/*
3433 * initialization verbs
3434 */
3435static struct hda_verb ad1882_init_verbs[] = {
3436 /* DACs; mute as default */
3437 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3438 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3439 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3440 /* Port-A (HP) mixer */
3441 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3442 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3443 /* Port-A pin */
3444 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3445 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3446 /* HP selector - select DAC2 */
3447 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
3448 /* Port-D (Line-out) mixer */
3449 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3450 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3451 /* Port-D pin */
3452 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3453 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3454 /* Mono-out mixer */
3455 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3456 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3457 /* Mono-out pin */
3458 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3459 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3460 /* Port-B (front mic) pin */
3461 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3462 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3463 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
3464 /* Port-C (line-in) pin */
3465 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3466 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3467 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
3468 /* Port-C mixer - mute as input */
3469 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3470 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3471 /* Port-E (mic-in) pin */
3472 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3473 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3474 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
3475 /* Port-E mixer - mute as input */
3476 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3477 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3478 /* Port-F (surround) */
3479 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3480 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3481 /* Port-G (CLFE) */
3482 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3483 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3484 /* Analog mixer; mute as default */
3485 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
3486 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3487 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3488 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3489 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3490 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3491 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3492 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
3493 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
3494 /* Analog Mix output amp */
3495 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3496 /* SPDIF output selector */
3497 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3498 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3499 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3500 { } /* end */
3501};
3502
Takashi Iwaicb53c622007-08-10 17:21:45 +02003503#ifdef CONFIG_SND_HDA_POWER_SAVE
3504static struct hda_amp_list ad1882_loopbacks[] = {
3505 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3506 { 0x20, HDA_INPUT, 1 }, /* Mic */
3507 { 0x20, HDA_INPUT, 4 }, /* Line */
3508 { 0x20, HDA_INPUT, 6 }, /* CD */
3509 { } /* end */
3510};
3511#endif
3512
Takashi Iwai0ac85512007-06-20 15:46:13 +02003513/* models */
3514enum {
3515 AD1882_3STACK,
3516 AD1882_6STACK,
3517 AD1882_MODELS
3518};
3519
3520static const char *ad1882_models[AD1986A_MODELS] = {
3521 [AD1882_3STACK] = "3stack",
3522 [AD1882_6STACK] = "6stack",
3523};
3524
3525
3526static int patch_ad1882(struct hda_codec *codec)
3527{
3528 struct ad198x_spec *spec;
3529 int board_config;
3530
3531 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3532 if (spec == NULL)
3533 return -ENOMEM;
3534
3535 mutex_init(&spec->amp_mutex);
3536 codec->spec = spec;
3537
3538 spec->multiout.max_channels = 6;
3539 spec->multiout.num_dacs = 3;
3540 spec->multiout.dac_nids = ad1882_dac_nids;
3541 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
3542 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
3543 spec->adc_nids = ad1882_adc_nids;
3544 spec->capsrc_nids = ad1882_capsrc_nids;
3545 spec->input_mux = &ad1882_capture_source;
3546 spec->num_mixers = 1;
3547 spec->mixers[0] = ad1882_base_mixers;
3548 spec->num_init_verbs = 1;
3549 spec->init_verbs[0] = ad1882_init_verbs;
3550 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003551#ifdef CONFIG_SND_HDA_POWER_SAVE
3552 spec->loopback.amplist = ad1882_loopbacks;
3553#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003554 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02003555
3556 codec->patch_ops = ad198x_patch_ops;
3557
3558 /* override some parameters */
3559 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
3560 ad1882_models, NULL);
3561 switch (board_config) {
3562 default:
3563 case AD1882_3STACK:
3564 spec->num_mixers = 2;
3565 spec->mixers[1] = ad1882_3stack_mixers;
3566 spec->channel_mode = ad1882_modes;
3567 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
3568 spec->need_dac_fix = 1;
3569 spec->multiout.max_channels = 2;
3570 spec->multiout.num_dacs = 1;
3571 break;
3572 case AD1882_6STACK:
3573 spec->num_mixers = 2;
3574 spec->mixers[1] = ad1882_6stack_mixers;
3575 break;
3576 }
3577 return 0;
3578}
3579
3580
3581/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07003582 * patch entries
3583 */
3584struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02003585 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003586 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02003587 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
3588 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003589 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07003590 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003591 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02003592 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07003593 {} /* terminator */
3594};