blob: 02643bce56349d6eb84fda8678ee441f2040d772 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <sound/core.h>
28#include "hda_codec.h"
29#include "hda_local.h"
Harvey Harrison3c9a3202008-02-29 11:59:26 +010030#include "hda_patch.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
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
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020066 unsigned int spdif_route;
Takashi Iwaid32410b12005-11-24 16:06:23 +010067
68 /* dynamic controls, init_verbs and input_mux */
69 struct auto_pin_cfg autocfg;
Takashi Iwai603c4012008-07-30 15:01:44 +020070 struct snd_array kctls;
Takashi Iwaid32410b12005-11-24 16:06:23 +010071 struct hda_input_mux private_imux;
Takashi Iwai41923e42007-10-22 17:20:10 +020072 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
Takashi Iwaicb53c622007-08-10 17:21:45 +020073
Takashi Iwai8ab78c72007-09-06 14:29:53 +020074 unsigned int jack_present :1;
75
Takashi Iwaicb53c622007-08-10 17:21:45 +020076#ifdef CONFIG_SND_HDA_POWER_SAVE
77 struct hda_loopback_check loopback;
78#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +010079 /* for virtual master */
80 hda_nid_t vmaster_nid;
Takashi Iwai2134ea42008-01-10 16:53:55 +010081 const char **slave_vols;
82 const char **slave_sws;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083};
84
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020085/*
86 * input MUX handling (common part)
87 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010088static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020089{
90 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
91 struct ad198x_spec *spec = codec->spec;
92
93 return snd_hda_input_mux_info(spec->input_mux, uinfo);
94}
95
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010096static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020097{
98 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
99 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100100 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200101
Takashi Iwai985be542005-11-02 18:26:49 +0100102 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200103 return 0;
104}
105
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100106static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200107{
108 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
109 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100110 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200111
112 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai2e5b9562005-11-21 16:36:15 +0100113 spec->capsrc_nids[adc_idx],
114 &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200115}
116
117/*
118 * initialization (common callbacks)
119 */
120static int ad198x_init(struct hda_codec *codec)
121{
122 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100123 int i;
124
125 for (i = 0; i < spec->num_init_verbs; i++)
126 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200127 return 0;
128}
129
Takashi Iwai2134ea42008-01-10 16:53:55 +0100130static const char *ad_slave_vols[] = {
131 "Front Playback Volume",
132 "Surround Playback Volume",
133 "Center Playback Volume",
134 "LFE Playback Volume",
135 "Side Playback Volume",
136 "Headphone Playback Volume",
137 "Mono Playback Volume",
Takashi Iwai628ed132008-01-25 11:56:57 +0100138 "Speaker Playback Volume",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100139 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100140 NULL
141};
142
143static const char *ad_slave_sws[] = {
144 "Front Playback Switch",
145 "Surround Playback Switch",
146 "Center Playback Switch",
147 "LFE Playback Switch",
148 "Side Playback Switch",
149 "Headphone Playback Switch",
150 "Mono Playback Switch",
Takashi Iwai628ed132008-01-25 11:56:57 +0100151 "Speaker Playback Switch",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100152 "IEC958 Playback Switch",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100153 NULL
154};
155
Takashi Iwai603c4012008-07-30 15:01:44 +0200156static void ad198x_free_kctls(struct hda_codec *codec);
157
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200158static int ad198x_build_controls(struct hda_codec *codec)
159{
160 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100161 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200162 int err;
163
Takashi Iwai985be542005-11-02 18:26:49 +0100164 for (i = 0; i < spec->num_mixers; i++) {
165 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
166 if (err < 0)
167 return err;
168 }
169 if (spec->multiout.dig_out_nid) {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200170 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100171 if (err < 0)
172 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100173 err = snd_hda_create_spdif_share_sw(codec,
174 &spec->multiout);
175 if (err < 0)
176 return err;
177 spec->multiout.share_spdif = 1;
Takashi Iwai985be542005-11-02 18:26:49 +0100178 }
179 if (spec->dig_in_nid) {
180 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
181 if (err < 0)
182 return err;
183 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100184
185 /* if we have no master control, let's create it */
186 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100187 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100188 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100189 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100190 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100191 vmaster_tlv,
Takashi Iwai2134ea42008-01-10 16:53:55 +0100192 (spec->slave_vols ?
193 spec->slave_vols : ad_slave_vols));
194 if (err < 0)
195 return err;
196 }
197 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
198 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
199 NULL,
200 (spec->slave_sws ?
201 spec->slave_sws : ad_slave_sws));
202 if (err < 0)
203 return err;
204 }
205
Takashi Iwai603c4012008-07-30 15:01:44 +0200206 ad198x_free_kctls(codec); /* no longer needed */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200207 return 0;
208}
209
Takashi Iwaicb53c622007-08-10 17:21:45 +0200210#ifdef CONFIG_SND_HDA_POWER_SAVE
211static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
212{
213 struct ad198x_spec *spec = codec->spec;
214 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
215}
216#endif
217
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200218/*
219 * Analog playback callbacks
220 */
221static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
222 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100223 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200224{
225 struct ad198x_spec *spec = codec->spec;
Takashi Iwai9a081602008-02-12 18:37:26 +0100226 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
227 hinfo);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200228}
229
230static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
231 struct hda_codec *codec,
232 unsigned int stream_tag,
233 unsigned int format,
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_prepare(codec, &spec->multiout, stream_tag,
238 format, substream);
239}
240
241static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
242 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100243 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200244{
245 struct ad198x_spec *spec = codec->spec;
246 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
247}
248
249/*
250 * Digital out
251 */
252static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
253 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100254 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200255{
256 struct ad198x_spec *spec = codec->spec;
257 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
258}
259
260static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
261 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100262 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200263{
264 struct ad198x_spec *spec = codec->spec;
265 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
266}
267
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200268static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
269 struct hda_codec *codec,
270 unsigned int stream_tag,
271 unsigned int format,
272 struct snd_pcm_substream *substream)
273{
274 struct ad198x_spec *spec = codec->spec;
275 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
276 format, substream);
277}
278
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200279/*
280 * Analog capture
281 */
282static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
283 struct hda_codec *codec,
284 unsigned int stream_tag,
285 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100286 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200287{
288 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100289 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
290 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200291 return 0;
292}
293
294static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
295 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100296 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200297{
298 struct ad198x_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +0100299 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200300 return 0;
301}
302
303
304/*
305 */
306static struct hda_pcm_stream ad198x_pcm_analog_playback = {
307 .substreams = 1,
308 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100309 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200310 .nid = 0, /* fill later */
311 .ops = {
312 .open = ad198x_playback_pcm_open,
313 .prepare = ad198x_playback_pcm_prepare,
314 .cleanup = ad198x_playback_pcm_cleanup
315 },
316};
317
318static struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100319 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200320 .channels_min = 2,
321 .channels_max = 2,
322 .nid = 0, /* fill later */
323 .ops = {
324 .prepare = ad198x_capture_pcm_prepare,
325 .cleanup = ad198x_capture_pcm_cleanup
326 },
327};
328
329static struct hda_pcm_stream ad198x_pcm_digital_playback = {
330 .substreams = 1,
331 .channels_min = 2,
332 .channels_max = 2,
333 .nid = 0, /* fill later */
334 .ops = {
335 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200336 .close = ad198x_dig_playback_pcm_close,
337 .prepare = ad198x_dig_playback_pcm_prepare
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200338 },
339};
340
Takashi Iwai985be542005-11-02 18:26:49 +0100341static struct hda_pcm_stream ad198x_pcm_digital_capture = {
342 .substreams = 1,
343 .channels_min = 2,
344 .channels_max = 2,
345 /* NID is set in alc_build_pcms */
346};
347
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200348static int ad198x_build_pcms(struct hda_codec *codec)
349{
350 struct ad198x_spec *spec = codec->spec;
351 struct hda_pcm *info = spec->pcm_rec;
352
353 codec->num_pcms = 1;
354 codec->pcm_info = info;
355
356 info->name = "AD198x Analog";
357 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
358 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
359 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
360 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100361 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
362 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200363
364 if (spec->multiout.dig_out_nid) {
365 info++;
366 codec->num_pcms++;
367 info->name = "AD198x Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +0100368 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200369 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
370 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100371 if (spec->dig_in_nid) {
372 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
373 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
374 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200375 }
376
377 return 0;
378}
379
Takashi Iwai603c4012008-07-30 15:01:44 +0200380static void ad198x_free_kctls(struct hda_codec *codec)
381{
382 struct ad198x_spec *spec = codec->spec;
383
384 if (spec->kctls.list) {
385 struct snd_kcontrol_new *kctl = spec->kctls.list;
386 int i;
387 for (i = 0; i < spec->kctls.used; i++)
388 kfree(kctl[i].name);
389 }
390 snd_array_free(&spec->kctls);
391}
392
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200393static void ad198x_free(struct hda_codec *codec)
394{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100395 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +0100396
Takashi Iwai603c4012008-07-30 15:01:44 +0200397 if (!spec)
398 return;
399
400 ad198x_free_kctls(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200401 kfree(codec->spec);
402}
403
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200404static struct hda_codec_ops ad198x_patch_ops = {
405 .build_controls = ad198x_build_controls,
406 .build_pcms = ad198x_build_pcms,
407 .init = ad198x_init,
408 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200409#ifdef CONFIG_SND_HDA_POWER_SAVE
410 .check_power_status = ad198x_check_power_status,
411#endif
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200412};
413
414
415/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100416 * EAPD control
417 * the private value = nid | (invert << 8)
418 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200419#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100420
421static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
422 struct snd_ctl_elem_value *ucontrol)
423{
424 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
425 struct ad198x_spec *spec = codec->spec;
426 int invert = (kcontrol->private_value >> 8) & 1;
427 if (invert)
428 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
429 else
430 ucontrol->value.integer.value[0] = spec->cur_eapd;
431 return 0;
432}
433
434static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
435 struct snd_ctl_elem_value *ucontrol)
436{
437 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
438 struct ad198x_spec *spec = codec->spec;
439 int invert = (kcontrol->private_value >> 8) & 1;
440 hda_nid_t nid = kcontrol->private_value & 0xff;
441 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100442 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwai18a815d2006-03-01 19:54:39 +0100443 if (invert)
444 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200445 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100446 return 0;
447 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200448 snd_hda_codec_write_cache(codec, nid,
449 0, AC_VERB_SET_EAPD_BTLENABLE,
450 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100451 return 1;
452}
453
Takashi Iwai9230d212006-03-13 13:49:49 +0100454static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
455 struct snd_ctl_elem_info *uinfo);
456static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
457 struct snd_ctl_elem_value *ucontrol);
458static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
459 struct snd_ctl_elem_value *ucontrol);
460
461
Takashi Iwai18a815d2006-03-01 19:54:39 +0100462/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200463 * AD1986A specific
464 */
465
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466#define AD1986A_SPDIF_OUT 0x02
467#define AD1986A_FRONT_DAC 0x03
468#define AD1986A_SURR_DAC 0x04
469#define AD1986A_CLFE_DAC 0x05
470#define AD1986A_ADC 0x06
471
472static hda_nid_t ad1986a_dac_nids[3] = {
473 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
474};
Takashi Iwai985be542005-11-02 18:26:49 +0100475static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +0100476static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477
478static struct hda_input_mux ad1986a_capture_source = {
479 .num_items = 7,
480 .items = {
481 { "Mic", 0x0 },
482 { "CD", 0x1 },
483 { "Aux", 0x3 },
484 { "Line", 0x4 },
485 { "Mix", 0x5 },
486 { "Mono", 0x6 },
487 { "Phone", 0x7 },
488 },
489};
490
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491
Takashi Iwai532d5382007-07-27 19:02:40 +0200492static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
493 .ops = &snd_hda_bind_vol,
494 .values = {
495 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
496 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
497 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
498 0
499 },
500};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
Takashi Iwai532d5382007-07-27 19:02:40 +0200502static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
503 .ops = &snd_hda_bind_sw,
504 .values = {
505 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
506 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
507 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
508 0
509 },
510};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
512/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 * mixers
514 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100515static struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200516 /*
517 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
518 */
519 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
520 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
522 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
523 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
524 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
525 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
526 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
527 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
528 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
529 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
530 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
531 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
532 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
533 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
534 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
535 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
536 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
537 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
538 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100539 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
541 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
542 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
543 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
544 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
545 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
546 {
547 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
548 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200549 .info = ad198x_mux_enum_info,
550 .get = ad198x_mux_enum_get,
551 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 },
553 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
554 { } /* end */
555};
556
Takashi Iwai9230d212006-03-13 13:49:49 +0100557/* additional mixers for 3stack mode */
558static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
559 {
560 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
561 .name = "Channel Mode",
562 .info = ad198x_ch_mode_info,
563 .get = ad198x_ch_mode_get,
564 .put = ad198x_ch_mode_put,
565 },
566 { } /* end */
567};
568
569/* laptop model - 2ch only */
570static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
571
Takashi Iwai20a45e82007-08-15 22:20:45 +0200572/* master controls both pins 0x1a and 0x1b */
573static struct hda_bind_ctls ad1986a_laptop_master_vol = {
574 .ops = &snd_hda_bind_vol,
575 .values = {
576 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
577 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
578 0,
579 },
580};
581
582static struct hda_bind_ctls ad1986a_laptop_master_sw = {
583 .ops = &snd_hda_bind_sw,
584 .values = {
585 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
586 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
587 0,
588 },
589};
590
Takashi Iwai9230d212006-03-13 13:49:49 +0100591static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
592 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
593 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200594 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
595 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100596 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
597 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
598 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
599 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
600 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
601 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
602 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
603 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100604 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai9230d212006-03-13 13:49:49 +0100605 /* HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
606 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
607 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
608 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
609 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
610 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
611 {
612 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
613 .name = "Capture Source",
614 .info = ad198x_mux_enum_info,
615 .get = ad198x_mux_enum_get,
616 .put = ad198x_mux_enum_put,
617 },
618 { } /* end */
619};
620
Takashi Iwai825aa9722006-03-17 10:50:49 +0100621/* laptop-eapd model - 2ch only */
622
Takashi Iwai825aa9722006-03-17 10:50:49 +0100623static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
624 .num_items = 3,
625 .items = {
626 { "Mic", 0x0 },
627 { "Internal Mic", 0x4 },
628 { "Mix", 0x5 },
629 },
630};
631
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100632static struct hda_input_mux ad1986a_automic_capture_source = {
633 .num_items = 2,
634 .items = {
635 { "Mic", 0x0 },
636 { "Mix", 0x5 },
637 },
638};
639
Takashi Iwai825aa9722006-03-17 10:50:49 +0100640static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200641 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
642 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai825aa9722006-03-17 10:50:49 +0100643 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
644 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai825aa9722006-03-17 10:50:49 +0100645 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
646 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100647 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai825aa9722006-03-17 10:50:49 +0100648 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
649 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
650 {
651 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
652 .name = "Capture Source",
653 .info = ad198x_mux_enum_info,
654 .get = ad198x_mux_enum_get,
655 .put = ad198x_mux_enum_put,
656 },
657 {
658 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
659 .name = "External Amplifier",
660 .info = ad198x_eapd_info,
661 .get = ad198x_eapd_get,
662 .put = ad198x_eapd_put,
663 .private_value = 0x1b | (1 << 8), /* port-D, inversed */
664 },
665 { } /* end */
666};
667
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100668/* re-connect the mic boost input according to the jack sensing */
669static void ad1986a_automic(struct hda_codec *codec)
670{
671 unsigned int present;
672 present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0);
673 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
674 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
675 (present & AC_PINSENSE_PRESENCE) ? 0 : 2);
676}
677
678#define AD1986A_MIC_EVENT 0x36
679
680static void ad1986a_automic_unsol_event(struct hda_codec *codec,
681 unsigned int res)
682{
683 if ((res >> 26) != AD1986A_MIC_EVENT)
684 return;
685 ad1986a_automic(codec);
686}
687
688static int ad1986a_automic_init(struct hda_codec *codec)
689{
690 ad198x_init(codec);
691 ad1986a_automic(codec);
692 return 0;
693}
694
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200695/* laptop-automute - 2ch only */
696
697static void ad1986a_update_hp(struct hda_codec *codec)
698{
699 struct ad198x_spec *spec = codec->spec;
700 unsigned int mute;
701
702 if (spec->jack_present)
703 mute = HDA_AMP_MUTE; /* mute internal speaker */
704 else
705 /* unmute internal speaker if necessary */
706 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
707 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
708 HDA_AMP_MUTE, mute);
709}
710
711static void ad1986a_hp_automute(struct hda_codec *codec)
712{
713 struct ad198x_spec *spec = codec->spec;
714 unsigned int present;
715
716 present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0);
Takashi Iwai53eb1b82007-10-17 10:09:32 +0200717 /* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
718 spec->jack_present = !(present & 0x80000000);
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200719 ad1986a_update_hp(codec);
720}
721
722#define AD1986A_HP_EVENT 0x37
723
724static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
725{
726 if ((res >> 26) != AD1986A_HP_EVENT)
727 return;
728 ad1986a_hp_automute(codec);
729}
730
731static int ad1986a_hp_init(struct hda_codec *codec)
732{
733 ad198x_init(codec);
734 ad1986a_hp_automute(codec);
735 return 0;
736}
737
738/* bind hp and internal speaker mute (with plug check) */
739static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
740 struct snd_ctl_elem_value *ucontrol)
741{
742 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
743 long *valp = ucontrol->value.integer.value;
744 int change;
745
746 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
747 HDA_AMP_MUTE,
748 valp[0] ? 0 : HDA_AMP_MUTE);
749 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
750 HDA_AMP_MUTE,
751 valp[1] ? 0 : HDA_AMP_MUTE);
752 if (change)
753 ad1986a_update_hp(codec);
754 return change;
755}
756
757static struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = {
758 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
759 {
760 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
761 .name = "Master Playback Switch",
762 .info = snd_hda_mixer_amp_switch_info,
763 .get = snd_hda_mixer_amp_switch_get,
764 .put = ad1986a_hp_master_sw_put,
765 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
766 },
767 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
768 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
769 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT),
770 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT),
771 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
772 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
773 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
774 HDA_CODEC_VOLUME("Beep Playback Volume", 0x18, 0x0, HDA_OUTPUT),
775 HDA_CODEC_MUTE("Beep Playback Switch", 0x18, 0x0, HDA_OUTPUT),
776 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
777 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
778 {
779 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
780 .name = "Capture Source",
781 .info = ad198x_mux_enum_info,
782 .get = ad198x_mux_enum_get,
783 .put = ad198x_mux_enum_put,
784 },
785 {
786 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
787 .name = "External Amplifier",
788 .info = ad198x_eapd_info,
789 .get = ad198x_eapd_get,
790 .put = ad198x_eapd_put,
791 .private_value = 0x1b | (1 << 8), /* port-D, inversed */
792 },
793 { } /* end */
794};
795
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796/*
797 * initialization verbs
798 */
799static struct hda_verb ad1986a_init_verbs[] = {
800 /* Front, Surround, CLFE DAC; mute as default */
801 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
802 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
803 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
804 /* Downmix - off */
805 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
806 /* HP, Line-Out, Surround, CLFE selectors */
807 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
808 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
809 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
810 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
811 /* Mono selector */
812 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
813 /* Mic selector: Mic 1/2 pin */
814 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
815 /* Line-in selector: Line-in */
816 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
817 /* Mic 1/2 swap */
818 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
819 /* Record selector: mic */
820 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
821 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
822 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
823 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
824 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
825 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
826 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
827 /* PC beep */
828 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
829 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
830 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
831 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
832 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
833 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
834 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200835 /* HP Pin */
836 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
837 /* Front, Surround, CLFE Pins */
838 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
839 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
840 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
841 /* Mono Pin */
842 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
843 /* Mic Pin */
844 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
845 /* Line, Aux, CD, Beep-In Pin */
846 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
847 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
848 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
849 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
850 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 { } /* end */
852};
853
Takashi Iwai9230d212006-03-13 13:49:49 +0100854static struct hda_verb ad1986a_ch2_init[] = {
855 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200856 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
857 /* Line-in selectors */
858 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100859 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200860 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
861 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
862 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100863 { } /* end */
864};
865
866static struct hda_verb ad1986a_ch4_init[] = {
867 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200868 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
869 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100870 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200871 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
872 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100873 { } /* end */
874};
875
876static struct hda_verb ad1986a_ch6_init[] = {
877 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200878 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
879 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100880 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200881 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
882 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100883 { } /* end */
884};
885
886static struct hda_channel_mode ad1986a_modes[3] = {
887 { 2, ad1986a_ch2_init },
888 { 4, ad1986a_ch4_init },
889 { 6, ad1986a_ch6_init },
890};
891
Takashi Iwai825aa9722006-03-17 10:50:49 +0100892/* eapd initialization */
893static struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +0100894 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa9722006-03-17 10:50:49 +0100895 {}
896};
897
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100898static struct hda_verb ad1986a_automic_verbs[] = {
899 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
900 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
901 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
902 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
903 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
904 {}
905};
906
Tobin Davisf36090f2007-01-08 11:07:12 +0100907/* Ultra initialization */
908static struct hda_verb ad1986a_ultra_init[] = {
909 /* eapd initialization */
910 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
911 /* CLFE -> Mic in */
912 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
913 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
914 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
915 { } /* end */
916};
917
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200918/* pin sensing on HP jack */
919static struct hda_verb ad1986a_hp_init_verbs[] = {
920 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
921 {}
922};
923
924
Takashi Iwai9230d212006-03-13 13:49:49 +0100925/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100926enum {
927 AD1986A_6STACK,
928 AD1986A_3STACK,
929 AD1986A_LAPTOP,
930 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200931 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +0100932 AD1986A_ULTRA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100933 AD1986A_MODELS
934};
Takashi Iwai9230d212006-03-13 13:49:49 +0100935
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100936static const char *ad1986a_models[AD1986A_MODELS] = {
937 [AD1986A_6STACK] = "6stack",
938 [AD1986A_3STACK] = "3stack",
939 [AD1986A_LAPTOP] = "laptop",
940 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200941 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +0100942 [AD1986A_ULTRA] = "ultra",
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100943};
944
945static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
946 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100947 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100948 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +0100949 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100950 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
951 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
952 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
953 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +0100954 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +0200955 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100956 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
957 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
958 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
959 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
960 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +0100961 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Takashi Iwai7db756f2007-12-24 14:36:09 +0100962 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
Tobin Davis18768992007-03-12 22:20:51 +0100963 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100964 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
965 SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),
966 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),
967 SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),
Tobin Davisf36090f2007-01-08 11:07:12 +0100968 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaiac3e3742007-12-17 17:14:18 +0100969 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +0100970 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100971 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200972 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100973 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +0100974 {}
975};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976
Takashi Iwaicb53c622007-08-10 17:21:45 +0200977#ifdef CONFIG_SND_HDA_POWER_SAVE
978static struct hda_amp_list ad1986a_loopbacks[] = {
979 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
980 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
981 { 0x15, HDA_OUTPUT, 0 }, /* CD */
982 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
983 { 0x17, HDA_OUTPUT, 0 }, /* Line */
984 { } /* end */
985};
986#endif
987
Takashi Iwai8c0d9642008-01-28 12:30:17 +0100988static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
989{
990 unsigned int conf = snd_hda_codec_read(codec, nid, 0,
991 AC_VERB_GET_CONFIG_DEFAULT, 0);
992 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
993}
994
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995static int patch_ad1986a(struct hda_codec *codec)
996{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200997 struct ad198x_spec *spec;
Takashi Iwai9230d212006-03-13 13:49:49 +0100998 int board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001000 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 if (spec == NULL)
1002 return -ENOMEM;
1003
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 codec->spec = spec;
1005
1006 spec->multiout.max_channels = 6;
1007 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1008 spec->multiout.dac_nids = ad1986a_dac_nids;
1009 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001010 spec->num_adc_nids = 1;
1011 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001012 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001013 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001014 spec->num_mixers = 1;
1015 spec->mixers[0] = ad1986a_mixers;
1016 spec->num_init_verbs = 1;
1017 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001018#ifdef CONFIG_SND_HDA_POWER_SAVE
1019 spec->loopback.amplist = ad1986a_loopbacks;
1020#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001021 spec->vmaster_nid = 0x1b;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001023 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024
Takashi Iwai9230d212006-03-13 13:49:49 +01001025 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001026 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1027 ad1986a_models,
1028 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001029 switch (board_config) {
1030 case AD1986A_3STACK:
1031 spec->num_mixers = 2;
1032 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001033 spec->num_init_verbs = 2;
1034 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001035 spec->channel_mode = ad1986a_modes;
1036 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001037 spec->need_dac_fix = 1;
1038 spec->multiout.max_channels = 2;
1039 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001040 break;
1041 case AD1986A_LAPTOP:
1042 spec->mixers[0] = ad1986a_laptop_mixers;
1043 spec->multiout.max_channels = 2;
1044 spec->multiout.num_dacs = 1;
1045 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1046 break;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001047 case AD1986A_LAPTOP_EAPD:
1048 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001049 spec->num_init_verbs = 3;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001050 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001051 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001052 spec->multiout.max_channels = 2;
1053 spec->multiout.num_dacs = 1;
1054 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001055 if (!is_jack_available(codec, 0x25))
1056 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001057 spec->input_mux = &ad1986a_automic_capture_source;
1058 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1059 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001060 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001061 case AD1986A_LAPTOP_AUTOMUTE:
1062 spec->mixers[0] = ad1986a_laptop_automute_mixers;
1063 spec->num_init_verbs = 3;
1064 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1065 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1066 spec->multiout.max_channels = 2;
1067 spec->multiout.num_dacs = 1;
1068 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001069 if (!is_jack_available(codec, 0x25))
1070 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001071 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1072 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1073 codec->patch_ops.init = ad1986a_hp_init;
1074 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001075 case AD1986A_ULTRA:
1076 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1077 spec->num_init_verbs = 2;
1078 spec->init_verbs[1] = ad1986a_ultra_init;
1079 spec->multiout.max_channels = 2;
1080 spec->multiout.num_dacs = 1;
1081 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1082 spec->multiout.dig_out_nid = 0;
1083 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001084 }
1085
Takashi Iwaid29240c2007-10-26 12:35:56 +02001086 /* AD1986A has a hardware problem that it can't share a stream
1087 * with multiple output pins. The copy of front to surrounds
1088 * causes noisy or silent outputs at a certain timing, e.g.
1089 * changing the volume.
1090 * So, let's disable the shared stream.
1091 */
1092 spec->multiout.no_share_stream = 1;
1093
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 return 0;
1095}
1096
1097/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001098 * AD1983 specific
1099 */
1100
1101#define AD1983_SPDIF_OUT 0x02
1102#define AD1983_DAC 0x03
1103#define AD1983_ADC 0x04
1104
1105static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001106static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001107static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001108
1109static struct hda_input_mux ad1983_capture_source = {
1110 .num_items = 4,
1111 .items = {
1112 { "Mic", 0x0 },
1113 { "Line", 0x1 },
1114 { "Mix", 0x2 },
1115 { "Mix Mono", 0x3 },
1116 },
1117};
1118
1119/*
1120 * SPDIF playback route
1121 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001122static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001123{
1124 static char *texts[] = { "PCM", "ADC" };
1125
1126 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1127 uinfo->count = 1;
1128 uinfo->value.enumerated.items = 2;
1129 if (uinfo->value.enumerated.item > 1)
1130 uinfo->value.enumerated.item = 1;
1131 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1132 return 0;
1133}
1134
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001135static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001136{
1137 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1138 struct ad198x_spec *spec = codec->spec;
1139
1140 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1141 return 0;
1142}
1143
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001144static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001145{
1146 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1147 struct ad198x_spec *spec = codec->spec;
1148
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001149 if (ucontrol->value.enumerated.item[0] > 1)
1150 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001151 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1152 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001153 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1154 AC_VERB_SET_CONNECT_SEL,
1155 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001156 return 1;
1157 }
1158 return 0;
1159}
1160
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001161static struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001162 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1163 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1164 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1165 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1166 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1167 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1168 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1169 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1170 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1171 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1172 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1173 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1174 HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x10, 1, 0x0, HDA_OUTPUT),
1175 HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x10, 1, 0x0, HDA_OUTPUT),
1176 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
1177 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1178 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1179 {
1180 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1181 .name = "Capture Source",
1182 .info = ad198x_mux_enum_info,
1183 .get = ad198x_mux_enum_get,
1184 .put = ad198x_mux_enum_put,
1185 },
1186 {
1187 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001188 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001189 .info = ad1983_spdif_route_info,
1190 .get = ad1983_spdif_route_get,
1191 .put = ad1983_spdif_route_put,
1192 },
1193 { } /* end */
1194};
1195
1196static struct hda_verb ad1983_init_verbs[] = {
1197 /* Front, HP, Mono; mute as default */
1198 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1199 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1200 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1201 /* Beep, PCM, Mic, Line-In: mute */
1202 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1203 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1204 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1205 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1206 /* Front, HP selectors; from Mix */
1207 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1208 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1209 /* Mono selector; from Mix */
1210 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1211 /* Mic selector; Mic */
1212 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1213 /* Line-in selector: Line-in */
1214 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1215 /* Mic boost: 0dB */
1216 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1217 /* Record selector: mic */
1218 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1219 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1220 /* SPDIF route: PCM */
1221 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1222 /* Front Pin */
1223 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1224 /* HP Pin */
1225 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1226 /* Mono Pin */
1227 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1228 /* Mic Pin */
1229 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1230 /* Line Pin */
1231 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1232 { } /* end */
1233};
1234
Takashi Iwaicb53c622007-08-10 17:21:45 +02001235#ifdef CONFIG_SND_HDA_POWER_SAVE
1236static struct hda_amp_list ad1983_loopbacks[] = {
1237 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1238 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1239 { } /* end */
1240};
1241#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001242
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001243static int patch_ad1983(struct hda_codec *codec)
1244{
1245 struct ad198x_spec *spec;
1246
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001247 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001248 if (spec == NULL)
1249 return -ENOMEM;
1250
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001251 codec->spec = spec;
1252
1253 spec->multiout.max_channels = 2;
1254 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1255 spec->multiout.dac_nids = ad1983_dac_nids;
1256 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001257 spec->num_adc_nids = 1;
1258 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001259 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001260 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001261 spec->num_mixers = 1;
1262 spec->mixers[0] = ad1983_mixers;
1263 spec->num_init_verbs = 1;
1264 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001265 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001266#ifdef CONFIG_SND_HDA_POWER_SAVE
1267 spec->loopback.amplist = ad1983_loopbacks;
1268#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001269 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001270
1271 codec->patch_ops = ad198x_patch_ops;
1272
1273 return 0;
1274}
1275
1276
1277/*
1278 * AD1981 HD specific
1279 */
1280
1281#define AD1981_SPDIF_OUT 0x02
1282#define AD1981_DAC 0x03
1283#define AD1981_ADC 0x04
1284
1285static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001286static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001287static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001288
1289/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
1290static struct hda_input_mux ad1981_capture_source = {
1291 .num_items = 7,
1292 .items = {
1293 { "Front Mic", 0x0 },
1294 { "Line", 0x1 },
1295 { "Mix", 0x2 },
1296 { "Mix Mono", 0x3 },
1297 { "CD", 0x4 },
1298 { "Mic", 0x6 },
1299 { "Aux", 0x7 },
1300 },
1301};
1302
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001303static struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001304 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1305 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1306 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1307 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1308 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1309 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1310 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1311 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1312 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1313 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1314 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1315 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1316 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1317 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1318 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1319 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1320 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1321 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1322 HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
1323 HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x0d, 1, 0x0, HDA_OUTPUT),
1324 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
1325 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
1326 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1327 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1328 {
1329 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1330 .name = "Capture Source",
1331 .info = ad198x_mux_enum_info,
1332 .get = ad198x_mux_enum_get,
1333 .put = ad198x_mux_enum_put,
1334 },
1335 /* identical with AD1983 */
1336 {
1337 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001338 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001339 .info = ad1983_spdif_route_info,
1340 .get = ad1983_spdif_route_get,
1341 .put = ad1983_spdif_route_put,
1342 },
1343 { } /* end */
1344};
1345
1346static struct hda_verb ad1981_init_verbs[] = {
1347 /* Front, HP, Mono; mute as default */
1348 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1349 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1350 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1351 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1352 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1353 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1354 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1355 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1356 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1357 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1358 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1359 /* Front, HP selectors; from Mix */
1360 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1361 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1362 /* Mono selector; from Mix */
1363 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1364 /* Mic Mixer; select Front Mic */
1365 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1366 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1367 /* Mic boost: 0dB */
1368 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1369 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1370 /* Record selector: Front mic */
1371 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1372 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1373 /* SPDIF route: PCM */
1374 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1375 /* Front Pin */
1376 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1377 /* HP Pin */
1378 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1379 /* Mono Pin */
1380 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1381 /* Front & Rear Mic Pins */
1382 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1383 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1384 /* Line Pin */
1385 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1386 /* Digital Beep */
1387 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1388 /* Line-Out as Input: disabled */
1389 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1390 { } /* end */
1391};
1392
Takashi Iwaicb53c622007-08-10 17:21:45 +02001393#ifdef CONFIG_SND_HDA_POWER_SAVE
1394static struct hda_amp_list ad1981_loopbacks[] = {
1395 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1396 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1397 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1398 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1399 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1400 { } /* end */
1401};
1402#endif
1403
Takashi Iwai18a815d2006-03-01 19:54:39 +01001404/*
1405 * Patch for HP nx6320
1406 *
Tobin Davis18768992007-03-12 22:20:51 +01001407 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001408 * speaker output enabled _and_ mute-LED off.
1409 */
1410
1411#define AD1981_HP_EVENT 0x37
1412#define AD1981_MIC_EVENT 0x38
1413
1414static struct hda_verb ad1981_hp_init_verbs[] = {
1415 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1416 /* pin sensing on HP and Mic jacks */
1417 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1418 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1419 {}
1420};
1421
1422/* turn on/off EAPD (+ mute HP) as a master switch */
1423static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1424 struct snd_ctl_elem_value *ucontrol)
1425{
1426 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1427 struct ad198x_spec *spec = codec->spec;
1428
1429 if (! ad198x_eapd_put(kcontrol, ucontrol))
1430 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001431 /* change speaker pin appropriately */
1432 snd_hda_codec_write(codec, 0x05, 0,
1433 AC_VERB_SET_PIN_WIDGET_CONTROL,
1434 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001435 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001436 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1437 HDA_AMP_MUTE,
1438 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001439 return 1;
1440}
1441
1442/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02001443static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
1444 .ops = &snd_hda_bind_vol,
1445 .values = {
1446 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1447 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1448 0
1449 },
1450};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001451
1452/* mute internal speaker if HP is plugged */
1453static void ad1981_hp_automute(struct hda_codec *codec)
1454{
1455 unsigned int present;
1456
1457 present = snd_hda_codec_read(codec, 0x06, 0,
1458 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
Takashi Iwai47fd8302007-08-10 17:11:07 +02001459 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1460 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001461}
1462
1463/* toggle input of built-in and mic jack appropriately */
1464static void ad1981_hp_automic(struct hda_codec *codec)
1465{
1466 static struct hda_verb mic_jack_on[] = {
1467 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1468 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1469 {}
1470 };
1471 static struct hda_verb mic_jack_off[] = {
1472 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1473 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1474 {}
1475 };
1476 unsigned int present;
1477
1478 present = snd_hda_codec_read(codec, 0x08, 0,
1479 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1480 if (present)
1481 snd_hda_sequence_write(codec, mic_jack_on);
1482 else
1483 snd_hda_sequence_write(codec, mic_jack_off);
1484}
1485
1486/* unsolicited event for HP jack sensing */
1487static void ad1981_hp_unsol_event(struct hda_codec *codec,
1488 unsigned int res)
1489{
1490 res >>= 26;
1491 switch (res) {
1492 case AD1981_HP_EVENT:
1493 ad1981_hp_automute(codec);
1494 break;
1495 case AD1981_MIC_EVENT:
1496 ad1981_hp_automic(codec);
1497 break;
1498 }
1499}
1500
1501static struct hda_input_mux ad1981_hp_capture_source = {
1502 .num_items = 3,
1503 .items = {
1504 { "Mic", 0x0 },
1505 { "Docking-Station", 0x1 },
1506 { "Mix", 0x2 },
1507 },
1508};
1509
1510static struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001511 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001512 {
1513 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1514 .name = "Master Playback Switch",
1515 .info = ad198x_eapd_info,
1516 .get = ad198x_eapd_get,
1517 .put = ad1981_hp_master_sw_put,
1518 .private_value = 0x05,
1519 },
1520 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1521 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1522#if 0
1523 /* FIXME: analog mic/line loopback doesn't work with my tests...
1524 * (although recording is OK)
1525 */
1526 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1527 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1528 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1529 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1530 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1531 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1532 /* FIXME: does this laptop have analog CD connection? */
1533 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1534 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1535#endif
1536 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1537 HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
1538 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1539 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1540 {
1541 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1542 .name = "Capture Source",
1543 .info = ad198x_mux_enum_info,
1544 .get = ad198x_mux_enum_get,
1545 .put = ad198x_mux_enum_put,
1546 },
1547 { } /* end */
1548};
1549
1550/* initialize jack-sensing, too */
1551static int ad1981_hp_init(struct hda_codec *codec)
1552{
1553 ad198x_init(codec);
1554 ad1981_hp_automute(codec);
1555 ad1981_hp_automic(codec);
1556 return 0;
1557}
1558
Tobin Davis18768992007-03-12 22:20:51 +01001559/* configuration for Toshiba Laptops */
1560static struct hda_verb ad1981_toshiba_init_verbs[] = {
1561 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1562 /* pin sensing on HP and Mic jacks */
1563 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1564 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1565 {}
1566};
1567
1568static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
1569 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1570 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1571 { }
1572};
1573
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001574/* configuration for Lenovo Thinkpad T60 */
1575static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
1576 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1577 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1578 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1579 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1580 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1581 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1582 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1583 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1584 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1585 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1586 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1587 {
1588 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1589 .name = "Capture Source",
1590 .info = ad198x_mux_enum_info,
1591 .get = ad198x_mux_enum_get,
1592 .put = ad198x_mux_enum_put,
1593 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001594 /* identical with AD1983 */
1595 {
1596 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1597 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1598 .info = ad1983_spdif_route_info,
1599 .get = ad1983_spdif_route_get,
1600 .put = ad1983_spdif_route_put,
1601 },
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001602 { } /* end */
1603};
1604
1605static struct hda_input_mux ad1981_thinkpad_capture_source = {
1606 .num_items = 3,
1607 .items = {
1608 { "Mic", 0x0 },
1609 { "Mix", 0x2 },
1610 { "CD", 0x4 },
1611 },
1612};
1613
Takashi Iwai18a815d2006-03-01 19:54:39 +01001614/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001615enum {
1616 AD1981_BASIC,
1617 AD1981_HP,
1618 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001619 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001620 AD1981_MODELS
1621};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001622
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001623static const char *ad1981_models[AD1981_MODELS] = {
1624 [AD1981_HP] = "hp",
1625 [AD1981_THINKPAD] = "thinkpad",
1626 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001627 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001628};
1629
1630static struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001631 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001632 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001633 /* All HP models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001634 SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001635 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001636 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001637 SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001638 /* HP nx6320 (reversed SSID, H/W bug) */
1639 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001640 {}
1641};
1642
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001643static int patch_ad1981(struct hda_codec *codec)
1644{
1645 struct ad198x_spec *spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001646 int board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001647
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001648 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001649 if (spec == NULL)
1650 return -ENOMEM;
1651
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001652 codec->spec = spec;
1653
1654 spec->multiout.max_channels = 2;
1655 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1656 spec->multiout.dac_nids = ad1981_dac_nids;
1657 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001658 spec->num_adc_nids = 1;
1659 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001660 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001661 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001662 spec->num_mixers = 1;
1663 spec->mixers[0] = ad1981_mixers;
1664 spec->num_init_verbs = 1;
1665 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001666 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001667#ifdef CONFIG_SND_HDA_POWER_SAVE
1668 spec->loopback.amplist = ad1981_loopbacks;
1669#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001670 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001671
1672 codec->patch_ops = ad198x_patch_ops;
1673
Takashi Iwai18a815d2006-03-01 19:54:39 +01001674 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001675 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1676 ad1981_models,
1677 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001678 switch (board_config) {
1679 case AD1981_HP:
1680 spec->mixers[0] = ad1981_hp_mixers;
1681 spec->num_init_verbs = 2;
1682 spec->init_verbs[1] = ad1981_hp_init_verbs;
1683 spec->multiout.dig_out_nid = 0;
1684 spec->input_mux = &ad1981_hp_capture_source;
1685
1686 codec->patch_ops.init = ad1981_hp_init;
1687 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1688 break;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001689 case AD1981_THINKPAD:
1690 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001691 spec->input_mux = &ad1981_thinkpad_capture_source;
1692 break;
Tobin Davis18768992007-03-12 22:20:51 +01001693 case AD1981_TOSHIBA:
1694 spec->mixers[0] = ad1981_hp_mixers;
1695 spec->mixers[1] = ad1981_toshiba_mixers;
1696 spec->num_init_verbs = 2;
1697 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1698 spec->multiout.dig_out_nid = 0;
1699 spec->input_mux = &ad1981_hp_capture_source;
1700 codec->patch_ops.init = ad1981_hp_init;
1701 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1702 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001703 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001704 return 0;
1705}
1706
1707
1708/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001709 * AD1988
1710 *
1711 * Output pins and routes
1712 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001713 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001714 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1715 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1716 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1717 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1718 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1719 * port-F 0x16 (mute) <- 0x2a <- 06
1720 * port-G 0x24 (mute) <- 0x27 <- 05
1721 * port-H 0x25 (mute) <- 0x28 <- 0a
1722 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1723 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001724 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1725 * (*) 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 +01001726 *
1727 * Input pins and routes
1728 *
1729 * pin boost mix input # / adc input #
1730 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1731 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1732 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1733 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1734 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1735 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1736 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1737 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1738 *
1739 *
1740 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001741 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001742 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001743 *
1744 * Inputs of Analog Mix (0x20)
1745 * 0:Port-B (front mic)
1746 * 1:Port-C/G/H (line-in)
1747 * 2:Port-A
1748 * 3:Port-D (line-in/2)
1749 * 4:Port-E/G/H (mic-in)
1750 * 5:Port-F (mic2-in)
1751 * 6:CD
1752 * 7:Beep
1753 *
1754 * ADC selection
1755 * 0:Port-A
1756 * 1:Port-B (front mic-in)
1757 * 2:Port-C (line-in)
1758 * 3:Port-F (mic2-in)
1759 * 4:Port-E (mic-in)
1760 * 5:CD
1761 * 6:Port-G
1762 * 7:Port-H
1763 * 8:Port-D (line-in/2)
1764 * 9:Mix
1765 *
1766 * Proposed pin assignments by the datasheet
1767 *
1768 * 6-stack
1769 * Port-A front headphone
1770 * B front mic-in
1771 * C rear line-in
1772 * D rear front-out
1773 * E rear mic-in
1774 * F rear surround
1775 * G rear CLFE
1776 * H rear side
1777 *
1778 * 3-stack
1779 * Port-A front headphone
1780 * B front mic
1781 * C rear line-in/surround
1782 * D rear front-out
1783 * E rear mic-in/CLFE
1784 *
1785 * laptop
1786 * Port-A headphone
1787 * B mic-in
1788 * C docking station
1789 * D internal speaker (with EAPD)
1790 * E/F quad mic array
1791 */
1792
1793
1794/* models */
1795enum {
1796 AD1988_6STACK,
1797 AD1988_6STACK_DIG,
1798 AD1988_3STACK,
1799 AD1988_3STACK_DIG,
1800 AD1988_LAPTOP,
1801 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01001802 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001803 AD1988_MODEL_LAST,
1804};
1805
Takashi Iwaid32410b12005-11-24 16:06:23 +01001806/* reivision id to check workarounds */
1807#define AD1988A_REV2 0x100200
1808
Takashi Iwai1a806f42006-07-03 15:58:16 +02001809#define is_rev2(codec) \
1810 ((codec)->vendor_id == 0x11d41988 && \
1811 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001812
1813/*
1814 * mixers
1815 */
1816
Takashi Iwaid32410b12005-11-24 16:06:23 +01001817static hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001818 0x04, 0x06, 0x05, 0x0a
1819};
1820
Takashi Iwaid32410b12005-11-24 16:06:23 +01001821static hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001822 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01001823};
1824
1825/* for AD1988A revision-2, DAC2-4 are swapped */
1826static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
1827 0x04, 0x05, 0x0a, 0x06
1828};
1829
1830static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001831 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01001832};
1833
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001834static hda_nid_t ad1988_adc_nids[3] = {
1835 0x08, 0x09, 0x0f
1836};
1837
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001838static hda_nid_t ad1988_capsrc_nids[3] = {
1839 0x0c, 0x0d, 0x0e
1840};
1841
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001842#define AD1988_SPDIF_OUT 0x02
1843#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001844#define AD1988_SPDIF_IN 0x07
1845
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001846static hda_nid_t ad1989b_slave_dig_outs[2] = {
1847 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI
1848};
1849
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001850static struct hda_input_mux ad1988_6stack_capture_source = {
1851 .num_items = 5,
1852 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001853 { "Front Mic", 0x1 }, /* port-B */
1854 { "Line", 0x2 }, /* port-C */
1855 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001856 { "CD", 0x5 },
1857 { "Mix", 0x9 },
1858 },
1859};
1860
1861static struct hda_input_mux ad1988_laptop_capture_source = {
1862 .num_items = 3,
1863 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001864 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001865 { "CD", 0x5 },
1866 { "Mix", 0x9 },
1867 },
1868};
1869
1870/*
1871 */
1872static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
1873 struct snd_ctl_elem_info *uinfo)
1874{
1875 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1876 struct ad198x_spec *spec = codec->spec;
1877 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
1878 spec->num_channel_mode);
1879}
1880
1881static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
1882 struct snd_ctl_elem_value *ucontrol)
1883{
1884 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1885 struct ad198x_spec *spec = codec->spec;
1886 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
1887 spec->num_channel_mode, spec->multiout.max_channels);
1888}
1889
1890static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
1891 struct snd_ctl_elem_value *ucontrol)
1892{
1893 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1894 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001895 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
1896 spec->num_channel_mode,
1897 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02001898 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02001899 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001900 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001901}
1902
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001903/* 6-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001904static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001905 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1906 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1907 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
1908 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
1909 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001910 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001911};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001912
Takashi Iwaid32410b12005-11-24 16:06:23 +01001913static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
1914 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1915 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1916 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
1917 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
1918 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001919 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001920};
1921
1922static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001923 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
1924 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
1925 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
1926 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
1927 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
1928 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
1929 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
1930
1931 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
1932 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
1933 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
1934 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
1935 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
1936 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
1937 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
1938 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
1939
1940 HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
1941 HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
1942
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001943 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001944 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
1945
1946 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
1947 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
1948
1949 { } /* end */
1950};
1951
1952/* 3-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001953static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001954 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01001955 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001956 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
1957 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001958 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001959};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001960
Takashi Iwaid32410b12005-11-24 16:06:23 +01001961static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
1962 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001963 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
1964 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
1965 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001966 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001967};
1968
1969static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001970 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01001971 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
1972 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
1973 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001974 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
1975 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
1976
1977 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
1978 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
1979 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
1980 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
1981 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
1982 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
1983 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
1984 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
1985
1986 HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
1987 HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
1988
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001989 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001990 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
1991
1992 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
1993 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
1994 {
1995 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1996 .name = "Channel Mode",
1997 .info = ad198x_ch_mode_info,
1998 .get = ad198x_ch_mode_get,
1999 .put = ad198x_ch_mode_put,
2000 },
2001
2002 { } /* end */
2003};
2004
2005/* laptop mode */
2006static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
2007 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2008 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2009 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2010
2011 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2012 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2013 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2014 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2015 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2016 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2017
2018 HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
2019 HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
2020
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002021 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002022 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2023
2024 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2025
2026 {
2027 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2028 .name = "External Amplifier",
Takashi Iwai18a815d2006-03-01 19:54:39 +01002029 .info = ad198x_eapd_info,
2030 .get = ad198x_eapd_get,
2031 .put = ad198x_eapd_put,
2032 .private_value = 0x12 | (1 << 8), /* port-D, inversed */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002033 },
2034
2035 { } /* end */
2036};
2037
2038/* capture */
2039static struct snd_kcontrol_new ad1988_capture_mixers[] = {
2040 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2041 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2042 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2043 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2044 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2045 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2046 {
2047 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2048 /* The multiple "Capture Source" controls confuse alsamixer
2049 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002050 */
2051 /* .name = "Capture Source", */
2052 .name = "Input Source",
2053 .count = 3,
2054 .info = ad198x_mux_enum_info,
2055 .get = ad198x_mux_enum_get,
2056 .put = ad198x_mux_enum_put,
2057 },
2058 { } /* end */
2059};
2060
2061static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2062 struct snd_ctl_elem_info *uinfo)
2063{
2064 static char *texts[] = {
2065 "PCM", "ADC1", "ADC2", "ADC3"
2066 };
2067 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2068 uinfo->count = 1;
2069 uinfo->value.enumerated.items = 4;
2070 if (uinfo->value.enumerated.item >= 4)
2071 uinfo->value.enumerated.item = 3;
2072 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2073 return 0;
2074}
2075
2076static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2077 struct snd_ctl_elem_value *ucontrol)
2078{
2079 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2080 unsigned int sel;
2081
Takashi Iwaibddcf542007-07-24 18:04:05 +02002082 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2083 AC_AMP_GET_INPUT);
2084 if (!(sel & 0x80))
2085 ucontrol->value.enumerated.item[0] = 0;
2086 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002087 sel = snd_hda_codec_read(codec, 0x0b, 0,
2088 AC_VERB_GET_CONNECT_SEL, 0);
2089 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002090 sel++;
2091 else
2092 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002093 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002094 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002095 return 0;
2096}
2097
2098static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2099 struct snd_ctl_elem_value *ucontrol)
2100{
2101 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002102 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002103 int change;
2104
Takashi Iwai35b26722007-05-05 12:17:17 +02002105 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002106 if (val > 3)
2107 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002108 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002109 sel = snd_hda_codec_read(codec, 0x1d, 0,
2110 AC_VERB_GET_AMP_GAIN_MUTE,
2111 AC_AMP_GET_INPUT);
2112 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002113 if (change) {
2114 snd_hda_codec_write_cache(codec, 0x1d, 0,
2115 AC_VERB_SET_AMP_GAIN_MUTE,
2116 AMP_IN_UNMUTE(0));
2117 snd_hda_codec_write_cache(codec, 0x1d, 0,
2118 AC_VERB_SET_AMP_GAIN_MUTE,
2119 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002120 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002121 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002122 sel = snd_hda_codec_read(codec, 0x1d, 0,
2123 AC_VERB_GET_AMP_GAIN_MUTE,
2124 AC_AMP_GET_INPUT | 0x01);
2125 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002126 if (change) {
2127 snd_hda_codec_write_cache(codec, 0x1d, 0,
2128 AC_VERB_SET_AMP_GAIN_MUTE,
2129 AMP_IN_MUTE(0));
2130 snd_hda_codec_write_cache(codec, 0x1d, 0,
2131 AC_VERB_SET_AMP_GAIN_MUTE,
2132 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002133 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002134 sel = snd_hda_codec_read(codec, 0x0b, 0,
2135 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2136 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002137 if (change)
2138 snd_hda_codec_write_cache(codec, 0x0b, 0,
2139 AC_VERB_SET_CONNECT_SEL,
2140 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002141 }
2142 return change;
2143}
2144
2145static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
2146 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2147 {
2148 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2149 .name = "IEC958 Playback Source",
2150 .info = ad1988_spdif_playback_source_info,
2151 .get = ad1988_spdif_playback_source_get,
2152 .put = ad1988_spdif_playback_source_put,
2153 },
2154 { } /* end */
2155};
2156
2157static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
2158 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2159 { } /* end */
2160};
2161
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002162static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
2163 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002164 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002165 { } /* end */
2166};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002167
2168/*
2169 * initialization verbs
2170 */
2171
2172/*
2173 * for 6-stack (+dig)
2174 */
2175static struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002176 /* Front, Surround, CLFE, side DAC; unmute as default */
2177 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2178 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2179 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2180 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002181 /* Port-A front headphon path */
2182 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2183 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2184 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2185 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2186 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2187 /* Port-D line-out path */
2188 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2189 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2190 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2191 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2192 /* Port-F surround path */
2193 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2194 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2195 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2196 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2197 /* Port-G CLFE path */
2198 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2199 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2200 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2201 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2202 /* Port-H side path */
2203 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2204 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2205 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2206 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2207 /* Mono out path */
2208 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2209 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2210 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2211 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2212 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2213 /* Port-B front mic-in path */
2214 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2215 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2216 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2217 /* Port-C line-in path */
2218 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2219 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2220 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2221 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2222 /* Port-E mic-in path */
2223 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2224 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2225 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2226 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002227 /* Analog CD Input */
2228 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002229 /* Analog Mix output amp */
2230 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002231
2232 { }
2233};
2234
2235static struct hda_verb ad1988_capture_init_verbs[] = {
2236 /* mute analog mix */
2237 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2238 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2239 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2240 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2241 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2242 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2243 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2244 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2245 /* select ADCs - front-mic */
2246 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2247 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2248 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
2249 /* ADCs; muted */
2250 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2251 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2252 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2253
2254 { }
2255};
2256
2257static struct hda_verb ad1988_spdif_init_verbs[] = {
2258 /* SPDIF out sel */
2259 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2260 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2261 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002262 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002263 /* SPDIF out pin */
2264 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002265
2266 { }
2267};
2268
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002269/* AD1989 has no ADC -> SPDIF route */
2270static struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002271 /* SPDIF-1 out pin */
2272 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002273 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002274 /* SPDIF-2/HDMI out pin */
2275 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2276 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002277 { }
2278};
2279
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002280/*
2281 * verbs for 3stack (+dig)
2282 */
2283static struct hda_verb ad1988_3stack_ch2_init[] = {
2284 /* set port-C to line-in */
2285 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2286 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2287 /* set port-E to mic-in */
2288 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2289 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2290 { } /* end */
2291};
2292
2293static struct hda_verb ad1988_3stack_ch6_init[] = {
2294 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002295 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002296 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002297 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002298 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002299 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002300 { } /* end */
2301};
2302
2303static struct hda_channel_mode ad1988_3stack_modes[2] = {
2304 { 2, ad1988_3stack_ch2_init },
2305 { 6, ad1988_3stack_ch6_init },
2306};
2307
2308static struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002309 /* Front, Surround, CLFE, side DAC; unmute as default */
2310 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2311 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2312 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2313 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002314 /* Port-A front headphon path */
2315 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2316 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2317 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2318 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2319 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2320 /* Port-D line-out path */
2321 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2322 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2323 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2324 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2325 /* Mono out path */
2326 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2327 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2328 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2329 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2330 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2331 /* Port-B front mic-in path */
2332 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2333 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2334 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002335 /* Port-C line-in/surround path - 6ch mode as default */
2336 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2337 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002338 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002339 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002340 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002341 /* Port-E mic-in/CLFE path - 6ch mode as default */
2342 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2343 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002344 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002345 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002346 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2347 /* mute analog mix */
2348 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2349 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2350 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2351 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2352 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2353 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2354 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2355 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2356 /* select ADCs - front-mic */
2357 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2358 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2359 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
2360 /* ADCs; muted */
2361 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2362 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2363 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002364 /* Analog Mix output amp */
2365 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002366 { }
2367};
2368
2369/*
2370 * verbs for laptop mode (+dig)
2371 */
2372static struct hda_verb ad1988_laptop_hp_on[] = {
2373 /* unmute port-A and mute port-D */
2374 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2375 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2376 { } /* end */
2377};
2378static struct hda_verb ad1988_laptop_hp_off[] = {
2379 /* mute port-A and unmute port-D */
2380 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2381 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2382 { } /* end */
2383};
2384
2385#define AD1988_HP_EVENT 0x01
2386
2387static struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002388 /* Front, Surround, CLFE, side DAC; unmute as default */
2389 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2390 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2391 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2392 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002393 /* Port-A front headphon path */
2394 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2395 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2396 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2397 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2398 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2399 /* unsolicited event for pin-sense */
2400 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2401 /* Port-D line-out path + EAPD */
2402 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2403 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2404 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2405 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2406 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2407 /* Mono out path */
2408 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2409 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2410 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2411 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2412 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2413 /* Port-B mic-in path */
2414 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2415 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2416 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2417 /* Port-C docking station - try to output */
2418 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2419 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2420 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2421 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2422 /* mute analog mix */
2423 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2424 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2425 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2426 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2427 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2428 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2429 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2430 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2431 /* select ADCs - mic */
2432 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2433 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2434 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
2435 /* ADCs; muted */
2436 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2437 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2438 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002439 /* Analog Mix output amp */
2440 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002441 { }
2442};
2443
2444static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2445{
2446 if ((res >> 26) != AD1988_HP_EVENT)
2447 return;
2448 if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31))
2449 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2450 else
2451 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2452}
2453
Takashi Iwaicb53c622007-08-10 17:21:45 +02002454#ifdef CONFIG_SND_HDA_POWER_SAVE
2455static struct hda_amp_list ad1988_loopbacks[] = {
2456 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2457 { 0x20, HDA_INPUT, 1 }, /* Line */
2458 { 0x20, HDA_INPUT, 4 }, /* Mic */
2459 { 0x20, HDA_INPUT, 6 }, /* CD */
2460 { } /* end */
2461};
2462#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002463
2464/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002465 * Automatic parse of I/O pins from the BIOS configuration
2466 */
2467
Takashi Iwaid32410b12005-11-24 16:06:23 +01002468enum {
2469 AD_CTL_WIDGET_VOL,
2470 AD_CTL_WIDGET_MUTE,
2471 AD_CTL_BIND_MUTE,
2472};
2473static struct snd_kcontrol_new ad1988_control_templates[] = {
2474 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2475 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2476 HDA_BIND_MUTE(NULL, 0, 0, 0),
2477};
2478
2479/* add dynamic controls */
2480static int add_control(struct ad198x_spec *spec, int type, const char *name,
2481 unsigned long val)
2482{
2483 struct snd_kcontrol_new *knew;
2484
Takashi Iwai603c4012008-07-30 15:01:44 +02002485 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2486 knew = snd_array_new(&spec->kctls);
2487 if (!knew)
2488 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002489 *knew = ad1988_control_templates[type];
2490 knew->name = kstrdup(name, GFP_KERNEL);
2491 if (! knew->name)
2492 return -ENOMEM;
2493 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002494 return 0;
2495}
2496
2497#define AD1988_PIN_CD_NID 0x18
2498#define AD1988_PIN_BEEP_NID 0x10
2499
2500static hda_nid_t ad1988_mixer_nids[8] = {
2501 /* A B C D E F G H */
2502 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2503};
2504
2505static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2506{
2507 static hda_nid_t idx_to_dac[8] = {
2508 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002509 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002510 };
2511 static hda_nid_t idx_to_dac_rev2[8] = {
2512 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002513 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002514 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002515 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002516 return idx_to_dac_rev2[idx];
2517 else
2518 return idx_to_dac[idx];
2519}
2520
2521static hda_nid_t ad1988_boost_nids[8] = {
2522 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2523};
2524
2525static int ad1988_pin_idx(hda_nid_t nid)
2526{
2527 static hda_nid_t ad1988_io_pins[8] = {
2528 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2529 };
2530 int i;
2531 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2532 if (ad1988_io_pins[i] == nid)
2533 return i;
2534 return 0; /* should be -1 */
2535}
2536
2537static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2538{
2539 static int loopback_idx[8] = {
2540 2, 0, 1, 3, 4, 5, 1, 4
2541 };
2542 switch (nid) {
2543 case AD1988_PIN_CD_NID:
2544 return 6;
2545 default:
2546 return loopback_idx[ad1988_pin_idx(nid)];
2547 }
2548}
2549
2550static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2551{
2552 static int adc_idx[8] = {
2553 0, 1, 2, 8, 4, 3, 6, 7
2554 };
2555 switch (nid) {
2556 case AD1988_PIN_CD_NID:
2557 return 5;
2558 default:
2559 return adc_idx[ad1988_pin_idx(nid)];
2560 }
2561}
2562
2563/* fill in the dac_nids table from the parsed pin configuration */
2564static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2565 const struct auto_pin_cfg *cfg)
2566{
2567 struct ad198x_spec *spec = codec->spec;
2568 int i, idx;
2569
2570 spec->multiout.dac_nids = spec->private_dac_nids;
2571
2572 /* check the pins hardwired to audio widget */
2573 for (i = 0; i < cfg->line_outs; i++) {
2574 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2575 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2576 }
2577 spec->multiout.num_dacs = cfg->line_outs;
2578 return 0;
2579}
2580
2581/* add playback controls from the parsed DAC table */
2582static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2583 const struct auto_pin_cfg *cfg)
2584{
2585 char name[32];
2586 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2587 hda_nid_t nid;
2588 int i, err;
2589
2590 for (i = 0; i < cfg->line_outs; i++) {
2591 hda_nid_t dac = spec->multiout.dac_nids[i];
2592 if (! dac)
2593 continue;
2594 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2595 if (i == 2) {
2596 /* Center/LFE */
2597 err = add_control(spec, AD_CTL_WIDGET_VOL,
2598 "Center Playback Volume",
2599 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2600 if (err < 0)
2601 return err;
2602 err = add_control(spec, AD_CTL_WIDGET_VOL,
2603 "LFE Playback Volume",
2604 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2605 if (err < 0)
2606 return err;
2607 err = add_control(spec, AD_CTL_BIND_MUTE,
2608 "Center Playback Switch",
2609 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2610 if (err < 0)
2611 return err;
2612 err = add_control(spec, AD_CTL_BIND_MUTE,
2613 "LFE Playback Switch",
2614 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2615 if (err < 0)
2616 return err;
2617 } else {
2618 sprintf(name, "%s Playback Volume", chname[i]);
2619 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2620 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2621 if (err < 0)
2622 return err;
2623 sprintf(name, "%s Playback Switch", chname[i]);
2624 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2625 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2626 if (err < 0)
2627 return err;
2628 }
2629 }
2630 return 0;
2631}
2632
2633/* add playback controls for speaker and HP outputs */
2634static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2635 const char *pfx)
2636{
2637 struct ad198x_spec *spec = codec->spec;
2638 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02002639 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002640 char name[32];
2641
2642 if (! pin)
2643 return 0;
2644
2645 idx = ad1988_pin_idx(pin);
2646 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02002647 /* check whether the corresponding DAC was already taken */
2648 for (i = 0; i < spec->autocfg.line_outs; i++) {
2649 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2650 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
2651 if (dac == nid)
2652 break;
2653 }
2654 if (i >= spec->autocfg.line_outs) {
2655 /* specify the DAC as the extra output */
2656 if (!spec->multiout.hp_nid)
2657 spec->multiout.hp_nid = nid;
2658 else
2659 spec->multiout.extra_out_nid[0] = nid;
2660 /* control HP volume/switch on the output mixer amp */
2661 sprintf(name, "%s Playback Volume", pfx);
2662 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2663 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2664 if (err < 0)
2665 return err;
2666 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01002667 nid = ad1988_mixer_nids[idx];
2668 sprintf(name, "%s Playback Switch", pfx);
2669 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2670 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2671 return err;
2672 return 0;
2673}
2674
2675/* create input playback/capture controls for the given pin */
2676static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2677 const char *ctlname, int boost)
2678{
2679 char name[32];
2680 int err, idx;
2681
2682 sprintf(name, "%s Playback Volume", ctlname);
2683 idx = ad1988_pin_to_loopback_idx(pin);
2684 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2685 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2686 return err;
2687 sprintf(name, "%s Playback Switch", ctlname);
2688 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2689 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2690 return err;
2691 if (boost) {
2692 hda_nid_t bnid;
2693 idx = ad1988_pin_idx(pin);
2694 bnid = ad1988_boost_nids[idx];
2695 if (bnid) {
2696 sprintf(name, "%s Boost", ctlname);
2697 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2698 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2699
2700 }
2701 }
2702 return 0;
2703}
2704
2705/* create playback/capture controls for input pins */
2706static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2707 const struct auto_pin_cfg *cfg)
2708{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002709 struct hda_input_mux *imux = &spec->private_imux;
2710 int i, err;
2711
2712 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002713 err = new_analog_input(spec, cfg->input_pins[i],
2714 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002715 i <= AUTO_PIN_FRONT_MIC);
2716 if (err < 0)
2717 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002718 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002719 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2720 imux->num_items++;
2721 }
2722 imux->items[imux->num_items].label = "Mix";
2723 imux->items[imux->num_items].index = 9;
2724 imux->num_items++;
2725
2726 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2727 "Analog Mix Playback Volume",
2728 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2729 return err;
2730 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2731 "Analog Mix Playback Switch",
2732 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2733 return err;
2734
2735 return 0;
2736}
2737
2738static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2739 hda_nid_t nid, int pin_type,
2740 int dac_idx)
2741{
2742 /* set as output */
2743 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2744 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2745 switch (nid) {
2746 case 0x11: /* port-A - DAC 04 */
2747 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2748 break;
2749 case 0x14: /* port-B - DAC 06 */
2750 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2751 break;
2752 case 0x15: /* port-C - DAC 05 */
2753 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2754 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002755 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002756 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2757 break;
2758 case 0x13: /* mono - DAC 04 */
2759 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2760 break;
2761 }
2762}
2763
2764static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2765{
2766 struct ad198x_spec *spec = codec->spec;
2767 int i;
2768
2769 for (i = 0; i < spec->autocfg.line_outs; i++) {
2770 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2771 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2772 }
2773}
2774
2775static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2776{
2777 struct ad198x_spec *spec = codec->spec;
2778 hda_nid_t pin;
2779
Takashi Iwai82bc9552006-03-21 11:24:42 +01002780 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002781 if (pin) /* connect to front */
2782 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002783 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002784 if (pin) /* connect to front */
2785 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2786}
2787
2788static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2789{
2790 struct ad198x_spec *spec = codec->spec;
2791 int i, idx;
2792
2793 for (i = 0; i < AUTO_PIN_LAST; i++) {
2794 hda_nid_t nid = spec->autocfg.input_pins[i];
2795 if (! nid)
2796 continue;
2797 switch (nid) {
2798 case 0x15: /* port-C */
2799 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2800 break;
2801 case 0x17: /* port-E */
2802 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2803 break;
2804 }
2805 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2806 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2807 if (nid != AD1988_PIN_CD_NID)
2808 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
2809 AMP_OUT_MUTE);
2810 idx = ad1988_pin_idx(nid);
2811 if (ad1988_boost_nids[idx])
2812 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
2813 AC_VERB_SET_AMP_GAIN_MUTE,
2814 AMP_OUT_ZERO);
2815 }
2816}
2817
2818/* parse the BIOS configuration and set up the alc_spec */
2819/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
2820static int ad1988_parse_auto_config(struct hda_codec *codec)
2821{
2822 struct ad198x_spec *spec = codec->spec;
2823 int err;
2824
Kailang Yangdf694da2005-12-05 19:42:22 +01002825 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002826 return err;
2827 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
2828 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002829 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002830 return 0; /* can't find valid BIOS pin config */
2831 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01002832 (err = ad1988_auto_create_extra_out(codec,
2833 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002834 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002835 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002836 "Headphone")) < 0 ||
2837 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
2838 return err;
2839
2840 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2841
2842 if (spec->autocfg.dig_out_pin)
2843 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2844 if (spec->autocfg.dig_in_pin)
2845 spec->dig_in_nid = AD1988_SPDIF_IN;
2846
Takashi Iwai603c4012008-07-30 15:01:44 +02002847 if (spec->kctls.list)
2848 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002849
2850 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
2851
2852 spec->input_mux = &spec->private_imux;
2853
2854 return 1;
2855}
2856
2857/* init callback for auto-configuration model -- overriding the default init */
2858static int ad1988_auto_init(struct hda_codec *codec)
2859{
2860 ad198x_init(codec);
2861 ad1988_auto_init_multi_out(codec);
2862 ad1988_auto_init_extra_out(codec);
2863 ad1988_auto_init_analog_input(codec);
2864 return 0;
2865}
2866
2867
2868/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002869 */
2870
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002871static const char *ad1988_models[AD1988_MODEL_LAST] = {
2872 [AD1988_6STACK] = "6stack",
2873 [AD1988_6STACK_DIG] = "6stack-dig",
2874 [AD1988_3STACK] = "3stack",
2875 [AD1988_3STACK_DIG] = "3stack-dig",
2876 [AD1988_LAPTOP] = "laptop",
2877 [AD1988_LAPTOP_DIG] = "laptop-dig",
2878 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002879};
2880
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002881static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002882 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002883 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02002884 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07002885 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002886 {}
2887};
2888
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002889static int patch_ad1988(struct hda_codec *codec)
2890{
2891 struct ad198x_spec *spec;
2892 int board_config;
2893
2894 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2895 if (spec == NULL)
2896 return -ENOMEM;
2897
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002898 codec->spec = spec;
2899
Takashi Iwai1a806f42006-07-03 15:58:16 +02002900 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002901 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
2902
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002903 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002904 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002905 if (board_config < 0) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002906 printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n");
2907 board_config = AD1988_AUTO;
2908 }
2909
2910 if (board_config == AD1988_AUTO) {
2911 /* automatic parse from the BIOS config */
2912 int err = ad1988_parse_auto_config(codec);
2913 if (err < 0) {
2914 ad198x_free(codec);
2915 return err;
2916 } else if (! err) {
2917 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
2918 board_config = AD1988_6STACK;
2919 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002920 }
2921
2922 switch (board_config) {
2923 case AD1988_6STACK:
2924 case AD1988_6STACK_DIG:
2925 spec->multiout.max_channels = 8;
2926 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002927 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002928 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
2929 else
2930 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002931 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002932 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002933 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002934 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
2935 else
2936 spec->mixers[0] = ad1988_6stack_mixers1;
2937 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002938 spec->num_init_verbs = 1;
2939 spec->init_verbs[0] = ad1988_6stack_init_verbs;
2940 if (board_config == AD1988_6STACK_DIG) {
2941 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2942 spec->dig_in_nid = AD1988_SPDIF_IN;
2943 }
2944 break;
2945 case AD1988_3STACK:
2946 case AD1988_3STACK_DIG:
2947 spec->multiout.max_channels = 6;
2948 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002949 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002950 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
2951 else
2952 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002953 spec->input_mux = &ad1988_6stack_capture_source;
2954 spec->channel_mode = ad1988_3stack_modes;
2955 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002956 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002957 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002958 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
2959 else
2960 spec->mixers[0] = ad1988_3stack_mixers1;
2961 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002962 spec->num_init_verbs = 1;
2963 spec->init_verbs[0] = ad1988_3stack_init_verbs;
2964 if (board_config == AD1988_3STACK_DIG)
2965 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2966 break;
2967 case AD1988_LAPTOP:
2968 case AD1988_LAPTOP_DIG:
2969 spec->multiout.max_channels = 2;
2970 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002971 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002972 spec->input_mux = &ad1988_laptop_capture_source;
2973 spec->num_mixers = 1;
2974 spec->mixers[0] = ad1988_laptop_mixers;
2975 spec->num_init_verbs = 1;
2976 spec->init_verbs[0] = ad1988_laptop_init_verbs;
2977 if (board_config == AD1988_LAPTOP_DIG)
2978 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2979 break;
2980 }
2981
Takashi Iwaid32410b12005-11-24 16:06:23 +01002982 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
2983 spec->adc_nids = ad1988_adc_nids;
2984 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002985 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
2986 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
2987 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002988 if (codec->vendor_id >= 0x11d4989a) {
2989 spec->mixers[spec->num_mixers++] =
2990 ad1989_spdif_out_mixers;
2991 spec->init_verbs[spec->num_init_verbs++] =
2992 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002993 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002994 } else {
2995 spec->mixers[spec->num_mixers++] =
2996 ad1988_spdif_out_mixers;
2997 spec->init_verbs[spec->num_init_verbs++] =
2998 ad1988_spdif_init_verbs;
2999 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003000 }
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003001 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003002 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
3003
3004 codec->patch_ops = ad198x_patch_ops;
3005 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003006 case AD1988_AUTO:
3007 codec->patch_ops.init = ad1988_auto_init;
3008 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003009 case AD1988_LAPTOP:
3010 case AD1988_LAPTOP_DIG:
3011 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3012 break;
3013 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003014#ifdef CONFIG_SND_HDA_POWER_SAVE
3015 spec->loopback.amplist = ad1988_loopbacks;
3016#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003017 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003018
3019 return 0;
3020}
3021
3022
3023/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003024 * AD1884 / AD1984
3025 *
3026 * port-B - front line/mic-in
3027 * port-E - aux in/out
3028 * port-F - aux in/out
3029 * port-C - rear line/mic-in
3030 * port-D - rear line/hp-out
3031 * port-A - front line/hp-out
3032 *
3033 * AD1984 = AD1884 + two digital mic-ins
3034 *
3035 * FIXME:
3036 * For simplicity, we share the single DAC for both HP and line-outs
3037 * right now. The inidividual playbacks could be easily implemented,
3038 * but no build-up framework is given, so far.
3039 */
3040
3041static hda_nid_t ad1884_dac_nids[1] = {
3042 0x04,
3043};
3044
3045static hda_nid_t ad1884_adc_nids[2] = {
3046 0x08, 0x09,
3047};
3048
3049static hda_nid_t ad1884_capsrc_nids[2] = {
3050 0x0c, 0x0d,
3051};
3052
3053#define AD1884_SPDIF_OUT 0x02
3054
3055static struct hda_input_mux ad1884_capture_source = {
3056 .num_items = 4,
3057 .items = {
3058 { "Front Mic", 0x0 },
3059 { "Mic", 0x1 },
3060 { "CD", 0x2 },
3061 { "Mix", 0x3 },
3062 },
3063};
3064
3065static struct snd_kcontrol_new ad1884_base_mixers[] = {
3066 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3067 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3068 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3069 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3070 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3071 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3072 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3073 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3074 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3075 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3076 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3077 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
3078 /*
3079 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
3080 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
3081 HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
3082 HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
3083 */
3084 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
3085 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3086 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3087 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3088 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3089 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3090 {
3091 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3092 /* The multiple "Capture Source" controls confuse alsamixer
3093 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003094 */
3095 /* .name = "Capture Source", */
3096 .name = "Input Source",
3097 .count = 2,
3098 .info = ad198x_mux_enum_info,
3099 .get = ad198x_mux_enum_get,
3100 .put = ad198x_mux_enum_put,
3101 },
3102 /* SPDIF controls */
3103 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3104 {
3105 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3106 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3107 /* identical with ad1983 */
3108 .info = ad1983_spdif_route_info,
3109 .get = ad1983_spdif_route_get,
3110 .put = ad1983_spdif_route_put,
3111 },
3112 { } /* end */
3113};
3114
3115static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3116 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3117 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3118 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003119 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003120 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003121 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003122 { } /* end */
3123};
3124
3125/*
3126 * initialization verbs
3127 */
3128static struct hda_verb ad1884_init_verbs[] = {
3129 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003130 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3131 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003132 /* Port-A (HP) mixer */
3133 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3134 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3135 /* Port-A pin */
3136 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3137 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3138 /* HP selector - select DAC2 */
3139 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3140 /* Port-D (Line-out) mixer */
3141 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3142 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3143 /* Port-D pin */
3144 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3145 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3146 /* Mono-out mixer */
3147 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3148 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3149 /* Mono-out pin */
3150 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3151 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3152 /* Mono selector */
3153 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3154 /* Port-B (front mic) pin */
3155 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3156 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3157 /* Port-C (rear mic) pin */
3158 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3159 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3160 /* Analog mixer; mute as default */
3161 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3162 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3163 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3164 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3165 /* Analog Mix output amp */
3166 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3167 /* SPDIF output selector */
3168 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3169 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3170 { } /* end */
3171};
3172
Takashi Iwaicb53c622007-08-10 17:21:45 +02003173#ifdef CONFIG_SND_HDA_POWER_SAVE
3174static struct hda_amp_list ad1884_loopbacks[] = {
3175 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3176 { 0x20, HDA_INPUT, 1 }, /* Mic */
3177 { 0x20, HDA_INPUT, 2 }, /* CD */
3178 { 0x20, HDA_INPUT, 4 }, /* Docking */
3179 { } /* end */
3180};
3181#endif
3182
Takashi Iwai2134ea42008-01-10 16:53:55 +01003183static const char *ad1884_slave_vols[] = {
3184 "PCM Playback Volume",
3185 "Mic Playback Volume",
3186 "Mono Playback Volume",
3187 "Front Mic Playback Volume",
3188 "Mic Playback Volume",
3189 "CD Playback Volume",
3190 "Internal Mic Playback Volume",
3191 "Docking Mic Playback Volume"
3192 "Beep Playback Volume",
Takashi Iwai4806ef02008-01-26 09:58:13 +01003193 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003194 NULL
3195};
3196
Takashi Iwai2bac6472007-05-18 18:21:41 +02003197static int patch_ad1884(struct hda_codec *codec)
3198{
3199 struct ad198x_spec *spec;
3200
3201 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3202 if (spec == NULL)
3203 return -ENOMEM;
3204
Takashi Iwai2bac6472007-05-18 18:21:41 +02003205 codec->spec = spec;
3206
3207 spec->multiout.max_channels = 2;
3208 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3209 spec->multiout.dac_nids = ad1884_dac_nids;
3210 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3211 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3212 spec->adc_nids = ad1884_adc_nids;
3213 spec->capsrc_nids = ad1884_capsrc_nids;
3214 spec->input_mux = &ad1884_capture_source;
3215 spec->num_mixers = 1;
3216 spec->mixers[0] = ad1884_base_mixers;
3217 spec->num_init_verbs = 1;
3218 spec->init_verbs[0] = ad1884_init_verbs;
3219 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003220#ifdef CONFIG_SND_HDA_POWER_SAVE
3221 spec->loopback.amplist = ad1884_loopbacks;
3222#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003223 spec->vmaster_nid = 0x04;
3224 /* we need to cover all playback volumes */
3225 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003226
3227 codec->patch_ops = ad198x_patch_ops;
3228
3229 return 0;
3230}
3231
3232/*
3233 * Lenovo Thinkpad T61/X61
3234 */
3235static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003236 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003237 .items = {
3238 { "Mic", 0x0 },
3239 { "Internal Mic", 0x1 },
3240 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003241 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003242 },
3243};
3244
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003245
3246/*
3247 * Dell Precision T3400
3248 */
3249static struct hda_input_mux ad1984_dell_desktop_capture_source = {
3250 .num_items = 3,
3251 .items = {
3252 { "Front Mic", 0x0 },
3253 { "Line-In", 0x1 },
3254 { "Mix", 0x3 },
3255 },
3256};
3257
3258
Takashi Iwai2bac6472007-05-18 18:21:41 +02003259static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3260 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3261 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3262 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3263 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3264 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3265 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3266 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3267 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3268 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3269 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003270 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3271 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003272 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003273 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3274 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003275 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3276 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3277 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3278 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3279 {
3280 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3281 /* The multiple "Capture Source" controls confuse alsamixer
3282 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003283 */
3284 /* .name = "Capture Source", */
3285 .name = "Input Source",
3286 .count = 2,
3287 .info = ad198x_mux_enum_info,
3288 .get = ad198x_mux_enum_get,
3289 .put = ad198x_mux_enum_put,
3290 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003291 /* SPDIF controls */
3292 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3293 {
3294 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3295 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3296 /* identical with ad1983 */
3297 .info = ad1983_spdif_route_info,
3298 .get = ad1983_spdif_route_get,
3299 .put = ad1983_spdif_route_put,
3300 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003301 { } /* end */
3302};
3303
3304/* additional verbs */
3305static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3306 /* Port-E (docking station mic) pin */
3307 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3308 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3309 /* docking mic boost */
3310 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3311 /* Analog mixer - docking mic; mute as default */
3312 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003313 /* enable EAPD bit */
3314 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003315 { } /* end */
3316};
3317
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003318/*
3319 * Dell Precision T3400
3320 */
3321static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
3322 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3323 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3324 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3325 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3326 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3327 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3328 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3329 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3330 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
3331 /*
3332 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
3333 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
3334 */
3335 HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
3336 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3337 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3338 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3339 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3340 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3341 {
3342 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3343 /* The multiple "Capture Source" controls confuse alsamixer
3344 * So call somewhat different..
3345 */
3346 /* .name = "Capture Source", */
3347 .name = "Input Source",
3348 .count = 2,
3349 .info = ad198x_mux_enum_info,
3350 .get = ad198x_mux_enum_get,
3351 .put = ad198x_mux_enum_put,
3352 },
3353 { } /* end */
3354};
3355
Takashi Iwai2bac6472007-05-18 18:21:41 +02003356/* Digial MIC ADC NID 0x05 + 0x06 */
3357static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3358 struct hda_codec *codec,
3359 unsigned int stream_tag,
3360 unsigned int format,
3361 struct snd_pcm_substream *substream)
3362{
3363 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3364 stream_tag, 0, format);
3365 return 0;
3366}
3367
3368static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3369 struct hda_codec *codec,
3370 struct snd_pcm_substream *substream)
3371{
Takashi Iwai888afa12008-03-18 09:57:50 +01003372 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003373 return 0;
3374}
3375
3376static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3377 .substreams = 2,
3378 .channels_min = 2,
3379 .channels_max = 2,
3380 .nid = 0x05,
3381 .ops = {
3382 .prepare = ad1984_pcm_dmic_prepare,
3383 .cleanup = ad1984_pcm_dmic_cleanup
3384 },
3385};
3386
3387static int ad1984_build_pcms(struct hda_codec *codec)
3388{
3389 struct ad198x_spec *spec = codec->spec;
3390 struct hda_pcm *info;
3391 int err;
3392
3393 err = ad198x_build_pcms(codec);
3394 if (err < 0)
3395 return err;
3396
3397 info = spec->pcm_rec + codec->num_pcms;
3398 codec->num_pcms++;
3399 info->name = "AD1984 Digital Mic";
3400 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3401 return 0;
3402}
3403
3404/* models */
3405enum {
3406 AD1984_BASIC,
3407 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003408 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003409 AD1984_MODELS
3410};
3411
3412static const char *ad1984_models[AD1984_MODELS] = {
3413 [AD1984_BASIC] = "basic",
3414 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003415 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003416};
3417
3418static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3419 /* Lenovo Thinkpad T61/X61 */
3420 SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003421 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003422 {}
3423};
3424
3425static int patch_ad1984(struct hda_codec *codec)
3426{
3427 struct ad198x_spec *spec;
3428 int board_config, err;
3429
3430 err = patch_ad1884(codec);
3431 if (err < 0)
3432 return err;
3433 spec = codec->spec;
3434 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3435 ad1984_models, ad1984_cfg_tbl);
3436 switch (board_config) {
3437 case AD1984_BASIC:
3438 /* additional digital mics */
3439 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3440 codec->patch_ops.build_pcms = ad1984_build_pcms;
3441 break;
3442 case AD1984_THINKPAD:
Jerone Youngebf00c52008-01-07 12:22:18 +01003443 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003444 spec->input_mux = &ad1984_thinkpad_capture_source;
3445 spec->mixers[0] = ad1984_thinkpad_mixers;
3446 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
3447 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003448 case AD1984_DELL_DESKTOP:
3449 spec->multiout.dig_out_nid = 0;
3450 spec->input_mux = &ad1984_dell_desktop_capture_source;
3451 spec->mixers[0] = ad1984_dell_desktop_mixers;
3452 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003453 }
3454 return 0;
3455}
3456
3457
3458/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003459 * AD1883 / AD1884A / AD1984A / AD1984B
3460 *
3461 * port-B (0x14) - front mic-in
3462 * port-E (0x1c) - rear mic-in
3463 * port-F (0x16) - CD / ext out
3464 * port-C (0x15) - rear line-in
3465 * port-D (0x12) - rear line-out
3466 * port-A (0x11) - front hp-out
3467 *
3468 * AD1984A = AD1884A + digital-mic
3469 * AD1883 = equivalent with AD1984A
3470 * AD1984B = AD1984A + extra SPDIF-out
3471 *
3472 * FIXME:
3473 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3474 */
3475
3476static hda_nid_t ad1884a_dac_nids[1] = {
3477 0x03,
3478};
3479
3480#define ad1884a_adc_nids ad1884_adc_nids
3481#define ad1884a_capsrc_nids ad1884_capsrc_nids
3482
3483#define AD1884A_SPDIF_OUT 0x02
3484
3485static struct hda_input_mux ad1884a_capture_source = {
3486 .num_items = 5,
3487 .items = {
3488 { "Front Mic", 0x0 },
3489 { "Mic", 0x4 },
3490 { "Line", 0x1 },
3491 { "CD", 0x2 },
3492 { "Mix", 0x3 },
3493 },
3494};
3495
3496static struct snd_kcontrol_new ad1884a_base_mixers[] = {
3497 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3498 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3499 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3500 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3501 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3502 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3503 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3504 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3505 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3506 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3507 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3508 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3509 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3510 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3511 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3512 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
3513 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3514 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
3515 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3516 HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
3517 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3518 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3519 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3520 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3521 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3522 {
3523 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3524 /* The multiple "Capture Source" controls confuse alsamixer
3525 * So call somewhat different..
3526 */
3527 /* .name = "Capture Source", */
3528 .name = "Input Source",
3529 .count = 2,
3530 .info = ad198x_mux_enum_info,
3531 .get = ad198x_mux_enum_get,
3532 .put = ad198x_mux_enum_put,
3533 },
3534 /* SPDIF controls */
3535 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3536 {
3537 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3538 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3539 /* identical with ad1983 */
3540 .info = ad1983_spdif_route_info,
3541 .get = ad1983_spdif_route_get,
3542 .put = ad1983_spdif_route_put,
3543 },
3544 { } /* end */
3545};
3546
3547/*
3548 * initialization verbs
3549 */
3550static struct hda_verb ad1884a_init_verbs[] = {
3551 /* DACs; unmute as default */
3552 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3553 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3554 /* Port-A (HP) mixer - route only from analog mixer */
3555 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3556 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3557 /* Port-A pin */
3558 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3559 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3560 /* Port-D (Line-out) mixer - route only from analog mixer */
3561 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3562 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3563 /* Port-D pin */
3564 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3565 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3566 /* Mono-out mixer - route only from analog mixer */
3567 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3568 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3569 /* Mono-out pin */
3570 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3571 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3572 /* Port-B (front mic) pin */
3573 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3574 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3575 /* Port-C (rear line-in) pin */
3576 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3577 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3578 /* Port-E (rear mic) pin */
3579 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3580 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3581 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
3582 /* Port-F (CD) pin */
3583 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3584 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3585 /* Analog mixer; mute as default */
3586 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3587 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3588 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3589 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3590 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
3591 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3592 /* Analog Mix output amp */
3593 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3594 /* capture sources */
3595 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
3596 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3597 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3598 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3599 /* SPDIF output amp */
3600 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3601 { } /* end */
3602};
3603
3604#ifdef CONFIG_SND_HDA_POWER_SAVE
3605static struct hda_amp_list ad1884a_loopbacks[] = {
3606 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3607 { 0x20, HDA_INPUT, 1 }, /* Mic */
3608 { 0x20, HDA_INPUT, 2 }, /* CD */
3609 { 0x20, HDA_INPUT, 4 }, /* Docking */
3610 { } /* end */
3611};
3612#endif
3613
3614/*
3615 * Laptop model
3616 *
3617 * Port A: Headphone jack
3618 * Port B: MIC jack
3619 * Port C: Internal MIC
3620 * Port D: Dock Line Out (if enabled)
3621 * Port E: Dock Line In (if enabled)
3622 * Port F: Internal speakers
3623 */
3624
3625static struct hda_input_mux ad1884a_laptop_capture_source = {
3626 .num_items = 4,
3627 .items = {
3628 { "Mic", 0x0 }, /* port-B */
3629 { "Internal Mic", 0x1 }, /* port-C */
3630 { "Dock Mic", 0x4 }, /* port-E */
3631 { "Mix", 0x3 },
3632 },
3633};
3634
3635static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
3636 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3637 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3638 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3639 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3640 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3641 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3642 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3643 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3644 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3645 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3646 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3647 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3648 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
3649 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3650 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
3651 HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3652 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3653 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3654 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3655 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3656 {
3657 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3658 /* The multiple "Capture Source" controls confuse alsamixer
3659 * So call somewhat different..
3660 */
3661 /* .name = "Capture Source", */
3662 .name = "Input Source",
3663 .count = 2,
3664 .info = ad198x_mux_enum_info,
3665 .get = ad198x_mux_enum_get,
3666 .put = ad198x_mux_enum_put,
3667 },
3668 { } /* end */
3669};
3670
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003671static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
3672 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3673 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3674 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3675 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003676 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3677 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02003678 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
3679 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003680 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3681 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003682 { } /* end */
3683};
3684
Takashi Iwaic5059252008-02-16 09:43:56 +01003685/* mute internal speaker if HP is plugged */
3686static void ad1884a_hp_automute(struct hda_codec *codec)
3687{
3688 unsigned int present;
3689
3690 present = snd_hda_codec_read(codec, 0x11, 0,
3691 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
3692 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3693 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3694 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3695 present ? 0x00 : 0x02);
3696}
3697
Takashi Iwai269ef192008-05-30 15:32:15 +02003698/* switch to external mic if plugged */
3699static void ad1884a_hp_automic(struct hda_codec *codec)
3700{
3701 unsigned int present;
3702
3703 present = snd_hda_codec_read(codec, 0x14, 0,
3704 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
3705 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
3706 present ? 0 : 1);
3707}
3708
Takashi Iwaic5059252008-02-16 09:43:56 +01003709#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02003710#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01003711
3712/* unsolicited event for HP jack sensing */
3713static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
3714{
Takashi Iwai269ef192008-05-30 15:32:15 +02003715 switch (res >> 26) {
3716 case AD1884A_HP_EVENT:
3717 ad1884a_hp_automute(codec);
3718 break;
3719 case AD1884A_MIC_EVENT:
3720 ad1884a_hp_automic(codec);
3721 break;
3722 }
Takashi Iwaic5059252008-02-16 09:43:56 +01003723}
3724
3725/* initialize jack-sensing, too */
3726static int ad1884a_hp_init(struct hda_codec *codec)
3727{
3728 ad198x_init(codec);
3729 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02003730 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01003731 return 0;
3732}
3733
3734/* additional verbs for laptop model */
3735static struct hda_verb ad1884a_laptop_verbs[] = {
3736 /* Port-A (HP) pin - always unmuted */
3737 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3738 /* Port-F (int speaker) mixer - route only from analog mixer */
3739 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3740 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3741 /* Port-F pin */
3742 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3743 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Takashi Iwai269ef192008-05-30 15:32:15 +02003744 /* Port-C pin - internal mic-in */
3745 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3746 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3747 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwaic5059252008-02-16 09:43:56 +01003748 /* analog mix */
3749 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3750 /* unsolicited event for pin-sense */
3751 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003752 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaic5059252008-02-16 09:43:56 +01003753 { } /* end */
3754};
3755
3756/*
Takashi Iwaif0813742008-03-18 12:13:03 +01003757 * Thinkpad X300
3758 * 0x11 - HP
3759 * 0x12 - speaker
3760 * 0x14 - mic-in
3761 * 0x17 - built-in mic
3762 */
3763
3764static struct hda_verb ad1984a_thinkpad_verbs[] = {
3765 /* HP unmute */
3766 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3767 /* analog mix */
3768 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3769 /* turn on EAPD */
3770 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
3771 /* unsolicited event for pin-sense */
3772 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
3773 /* internal mic - dmic */
3774 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02003775 /* set magic COEFs for dmic */
3776 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
3777 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01003778 { } /* end */
3779};
3780
3781static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
3782 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3783 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3784 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3785 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3786 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3787 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3788 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3789 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
3790 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3791 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
3792 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3793 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3794 {
3795 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3796 .name = "Capture Source",
3797 .info = ad198x_mux_enum_info,
3798 .get = ad198x_mux_enum_get,
3799 .put = ad198x_mux_enum_put,
3800 },
3801 { } /* end */
3802};
3803
3804static struct hda_input_mux ad1984a_thinkpad_capture_source = {
3805 .num_items = 3,
3806 .items = {
3807 { "Mic", 0x0 },
3808 { "Internal Mic", 0x5 },
3809 { "Mix", 0x3 },
3810 },
3811};
3812
3813/* mute internal speaker if HP is plugged */
3814static void ad1984a_thinkpad_automute(struct hda_codec *codec)
3815{
3816 unsigned int present;
3817
3818 present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0)
3819 & AC_PINSENSE_PRESENCE;
3820 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
3821 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3822}
3823
3824/* unsolicited event for HP jack sensing */
3825static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
3826 unsigned int res)
3827{
3828 if ((res >> 26) != AD1884A_HP_EVENT)
3829 return;
3830 ad1984a_thinkpad_automute(codec);
3831}
3832
3833/* initialize jack-sensing, too */
3834static int ad1984a_thinkpad_init(struct hda_codec *codec)
3835{
3836 ad198x_init(codec);
3837 ad1984a_thinkpad_automute(codec);
3838 return 0;
3839}
3840
3841/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003842 */
3843
3844enum {
3845 AD1884A_DESKTOP,
3846 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003847 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01003848 AD1884A_THINKPAD,
Takashi Iwaic5059252008-02-16 09:43:56 +01003849 AD1884A_MODELS
3850};
3851
3852static const char *ad1884a_models[AD1884A_MODELS] = {
3853 [AD1884A_DESKTOP] = "desktop",
3854 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003855 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01003856 [AD1884A_THINKPAD] = "thinkpad",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003857};
3858
3859static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
3860 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01003861 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003862 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01003863};
3864
3865static int patch_ad1884a(struct hda_codec *codec)
3866{
3867 struct ad198x_spec *spec;
3868 int board_config;
3869
3870 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3871 if (spec == NULL)
3872 return -ENOMEM;
3873
Takashi Iwaic5059252008-02-16 09:43:56 +01003874 codec->spec = spec;
3875
3876 spec->multiout.max_channels = 2;
3877 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
3878 spec->multiout.dac_nids = ad1884a_dac_nids;
3879 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
3880 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
3881 spec->adc_nids = ad1884a_adc_nids;
3882 spec->capsrc_nids = ad1884a_capsrc_nids;
3883 spec->input_mux = &ad1884a_capture_source;
3884 spec->num_mixers = 1;
3885 spec->mixers[0] = ad1884a_base_mixers;
3886 spec->num_init_verbs = 1;
3887 spec->init_verbs[0] = ad1884a_init_verbs;
3888 spec->spdif_route = 0;
3889#ifdef CONFIG_SND_HDA_POWER_SAVE
3890 spec->loopback.amplist = ad1884a_loopbacks;
3891#endif
3892 codec->patch_ops = ad198x_patch_ops;
3893
3894 /* override some parameters */
3895 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003896 ad1884a_models,
3897 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01003898 switch (board_config) {
3899 case AD1884A_LAPTOP:
3900 spec->mixers[0] = ad1884a_laptop_mixers;
3901 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
3902 spec->multiout.dig_out_nid = 0;
3903 spec->input_mux = &ad1884a_laptop_capture_source;
3904 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
3905 codec->patch_ops.init = ad1884a_hp_init;
3906 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003907 case AD1884A_MOBILE:
3908 spec->mixers[0] = ad1884a_mobile_mixers;
3909 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
3910 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003911 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
3912 codec->patch_ops.init = ad1884a_hp_init;
3913 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01003914 case AD1884A_THINKPAD:
3915 spec->mixers[0] = ad1984a_thinkpad_mixers;
3916 spec->init_verbs[spec->num_init_verbs++] =
3917 ad1984a_thinkpad_verbs;
3918 spec->multiout.dig_out_nid = 0;
3919 spec->input_mux = &ad1984a_thinkpad_capture_source;
3920 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
3921 codec->patch_ops.init = ad1984a_thinkpad_init;
3922 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01003923 }
3924
3925 return 0;
3926}
3927
3928
3929/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02003930 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02003931 *
3932 * port-A - front hp-out
3933 * port-B - front mic-in
3934 * port-C - rear line-in, shared surr-out (3stack)
3935 * port-D - rear line-out
3936 * port-E - rear mic-in, shared clfe-out (3stack)
3937 * port-F - rear surr-out (6stack)
3938 * port-G - rear clfe-out (6stack)
3939 */
3940
3941static hda_nid_t ad1882_dac_nids[3] = {
3942 0x04, 0x03, 0x05
3943};
3944
3945static hda_nid_t ad1882_adc_nids[2] = {
3946 0x08, 0x09,
3947};
3948
3949static hda_nid_t ad1882_capsrc_nids[2] = {
3950 0x0c, 0x0d,
3951};
3952
3953#define AD1882_SPDIF_OUT 0x02
3954
3955/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
3956static struct hda_input_mux ad1882_capture_source = {
3957 .num_items = 5,
3958 .items = {
3959 { "Front Mic", 0x1 },
3960 { "Mic", 0x4 },
3961 { "Line", 0x2 },
3962 { "CD", 0x3 },
3963 { "Mix", 0x7 },
3964 },
3965};
3966
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02003967/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
3968static struct hda_input_mux ad1882a_capture_source = {
3969 .num_items = 5,
3970 .items = {
3971 { "Front Mic", 0x1 },
3972 { "Mic", 0x4},
3973 { "Line", 0x2 },
3974 { "Digital Mic", 0x06 },
3975 { "Mix", 0x7 },
3976 },
3977};
3978
Takashi Iwai0ac85512007-06-20 15:46:13 +02003979static struct snd_kcontrol_new ad1882_base_mixers[] = {
3980 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3981 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
3982 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
3983 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
3984 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3985 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3986 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3987 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02003988
Takashi Iwai0ac85512007-06-20 15:46:13 +02003989 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
3990 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
3991 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
3992 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3993 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3994 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3995 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3996 {
3997 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3998 /* The multiple "Capture Source" controls confuse alsamixer
3999 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004000 */
4001 /* .name = "Capture Source", */
4002 .name = "Input Source",
4003 .count = 2,
4004 .info = ad198x_mux_enum_info,
4005 .get = ad198x_mux_enum_get,
4006 .put = ad198x_mux_enum_put,
4007 },
4008 /* SPDIF controls */
4009 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4010 {
4011 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4012 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4013 /* identical with ad1983 */
4014 .info = ad1983_spdif_route_info,
4015 .get = ad1983_spdif_route_get,
4016 .put = ad1983_spdif_route_put,
4017 },
4018 { } /* end */
4019};
4020
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004021static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
4022 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4023 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4024 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4025 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4026 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4027 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4028 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4029 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
4030 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
4031 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
4032 { } /* end */
4033};
4034
4035static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
4036 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4037 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4038 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4039 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4040 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4041 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4042 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4043 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
4044 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
4045 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
4046 HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
4047 { } /* end */
4048};
4049
Takashi Iwai0ac85512007-06-20 15:46:13 +02004050static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
4051 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4052 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4053 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4054 {
4055 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4056 .name = "Channel Mode",
4057 .info = ad198x_ch_mode_info,
4058 .get = ad198x_ch_mode_get,
4059 .put = ad198x_ch_mode_put,
4060 },
4061 { } /* end */
4062};
4063
4064static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
4065 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4066 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4067 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4068 { } /* end */
4069};
4070
4071static struct hda_verb ad1882_ch2_init[] = {
4072 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4073 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4074 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4075 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4076 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4077 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4078 { } /* end */
4079};
4080
4081static struct hda_verb ad1882_ch4_init[] = {
4082 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4083 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4084 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4085 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4086 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4087 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4088 { } /* end */
4089};
4090
4091static struct hda_verb ad1882_ch6_init[] = {
4092 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4093 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4094 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4095 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4096 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4097 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4098 { } /* end */
4099};
4100
4101static struct hda_channel_mode ad1882_modes[3] = {
4102 { 2, ad1882_ch2_init },
4103 { 4, ad1882_ch4_init },
4104 { 6, ad1882_ch6_init },
4105};
4106
4107/*
4108 * initialization verbs
4109 */
4110static struct hda_verb ad1882_init_verbs[] = {
4111 /* DACs; mute as default */
4112 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4113 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4114 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4115 /* Port-A (HP) mixer */
4116 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4117 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4118 /* Port-A pin */
4119 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4120 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4121 /* HP selector - select DAC2 */
4122 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4123 /* Port-D (Line-out) mixer */
4124 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4125 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4126 /* Port-D pin */
4127 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4128 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4129 /* Mono-out mixer */
4130 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4131 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4132 /* Mono-out pin */
4133 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4134 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4135 /* Port-B (front mic) pin */
4136 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4137 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4138 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4139 /* Port-C (line-in) pin */
4140 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4141 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4142 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4143 /* Port-C mixer - mute as input */
4144 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4145 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4146 /* Port-E (mic-in) pin */
4147 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4148 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4149 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4150 /* Port-E mixer - mute as input */
4151 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4152 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4153 /* Port-F (surround) */
4154 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4155 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4156 /* Port-G (CLFE) */
4157 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4158 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4159 /* Analog mixer; mute as default */
4160 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4161 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4162 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4163 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4164 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4165 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4166 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4167 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4168 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4169 /* Analog Mix output amp */
4170 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4171 /* SPDIF output selector */
4172 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4173 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4174 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4175 { } /* end */
4176};
4177
Takashi Iwaicb53c622007-08-10 17:21:45 +02004178#ifdef CONFIG_SND_HDA_POWER_SAVE
4179static struct hda_amp_list ad1882_loopbacks[] = {
4180 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4181 { 0x20, HDA_INPUT, 1 }, /* Mic */
4182 { 0x20, HDA_INPUT, 4 }, /* Line */
4183 { 0x20, HDA_INPUT, 6 }, /* CD */
4184 { } /* end */
4185};
4186#endif
4187
Takashi Iwai0ac85512007-06-20 15:46:13 +02004188/* models */
4189enum {
4190 AD1882_3STACK,
4191 AD1882_6STACK,
4192 AD1882_MODELS
4193};
4194
4195static const char *ad1882_models[AD1986A_MODELS] = {
4196 [AD1882_3STACK] = "3stack",
4197 [AD1882_6STACK] = "6stack",
4198};
4199
4200
4201static int patch_ad1882(struct hda_codec *codec)
4202{
4203 struct ad198x_spec *spec;
4204 int board_config;
4205
4206 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4207 if (spec == NULL)
4208 return -ENOMEM;
4209
Takashi Iwai0ac85512007-06-20 15:46:13 +02004210 codec->spec = spec;
4211
4212 spec->multiout.max_channels = 6;
4213 spec->multiout.num_dacs = 3;
4214 spec->multiout.dac_nids = ad1882_dac_nids;
4215 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
4216 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
4217 spec->adc_nids = ad1882_adc_nids;
4218 spec->capsrc_nids = ad1882_capsrc_nids;
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004219 if (codec->vendor_id == 0x11d1882)
4220 spec->input_mux = &ad1882_capture_source;
4221 else
4222 spec->input_mux = &ad1882a_capture_source;
4223 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004224 spec->mixers[0] = ad1882_base_mixers;
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004225 if (codec->vendor_id == 0x11d1882)
4226 spec->mixers[1] = ad1882_loopback_mixers;
4227 else
4228 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004229 spec->num_init_verbs = 1;
4230 spec->init_verbs[0] = ad1882_init_verbs;
4231 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02004232#ifdef CONFIG_SND_HDA_POWER_SAVE
4233 spec->loopback.amplist = ad1882_loopbacks;
4234#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01004235 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004236
4237 codec->patch_ops = ad198x_patch_ops;
4238
4239 /* override some parameters */
4240 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
4241 ad1882_models, NULL);
4242 switch (board_config) {
4243 default:
4244 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004245 spec->num_mixers = 3;
4246 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004247 spec->channel_mode = ad1882_modes;
4248 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
4249 spec->need_dac_fix = 1;
4250 spec->multiout.max_channels = 2;
4251 spec->multiout.num_dacs = 1;
4252 break;
4253 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004254 spec->num_mixers = 3;
4255 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004256 break;
4257 }
4258 return 0;
4259}
4260
4261
4262/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004263 * patch entries
4264 */
4265struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004266 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02004267 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004268 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004269 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004270 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
4271 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02004272 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
4273 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004274 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004275 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01004276 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02004277 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004278 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02004279 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
4280 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004281 {} /* terminator */
4282};