blob: b168028414686718e715c5fc3bd4f0a898e6671b [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"
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010030#include "hda_beep.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;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010035 unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
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 Iwaic5a4bcd2009-02-06 17:22:05 +0100158/* additional beep mixers; the actual parameters are overwritten at build */
159static struct snd_kcontrol_new ad_beep_mixer[] = {
160 HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
161 HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_OUTPUT),
162 { } /* end */
163};
164
165#define set_beep_amp(spec, nid, idx, dir) \
166 ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
167
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200168static int ad198x_build_controls(struct hda_codec *codec)
169{
170 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100171 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200172 int err;
173
Takashi Iwai985be542005-11-02 18:26:49 +0100174 for (i = 0; i < spec->num_mixers; i++) {
175 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
176 if (err < 0)
177 return err;
178 }
179 if (spec->multiout.dig_out_nid) {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200180 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100181 if (err < 0)
182 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100183 err = snd_hda_create_spdif_share_sw(codec,
184 &spec->multiout);
185 if (err < 0)
186 return err;
187 spec->multiout.share_spdif = 1;
Takashi Iwai985be542005-11-02 18:26:49 +0100188 }
189 if (spec->dig_in_nid) {
190 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
191 if (err < 0)
192 return err;
193 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100194
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100195 /* create beep controls if needed */
196 if (spec->beep_amp) {
197 struct snd_kcontrol_new *knew;
198 for (knew = ad_beep_mixer; knew->name; knew++) {
199 struct snd_kcontrol *kctl;
200 kctl = snd_ctl_new1(knew, codec);
201 if (!kctl)
202 return -ENOMEM;
203 kctl->private_value = spec->beep_amp;
204 err = snd_hda_ctl_add(codec, kctl);
205 if (err < 0)
206 return err;
207 }
208 }
209
Takashi Iwai2134ea42008-01-10 16:53:55 +0100210 /* if we have no master control, let's create it */
211 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100212 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100213 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100214 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100215 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100216 vmaster_tlv,
Takashi Iwai2134ea42008-01-10 16:53:55 +0100217 (spec->slave_vols ?
218 spec->slave_vols : ad_slave_vols));
219 if (err < 0)
220 return err;
221 }
222 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
223 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
224 NULL,
225 (spec->slave_sws ?
226 spec->slave_sws : ad_slave_sws));
227 if (err < 0)
228 return err;
229 }
230
Takashi Iwai603c4012008-07-30 15:01:44 +0200231 ad198x_free_kctls(codec); /* no longer needed */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200232 return 0;
233}
234
Takashi Iwaicb53c622007-08-10 17:21:45 +0200235#ifdef CONFIG_SND_HDA_POWER_SAVE
236static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
237{
238 struct ad198x_spec *spec = codec->spec;
239 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
240}
241#endif
242
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200243/*
244 * Analog playback callbacks
245 */
246static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
247 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100248 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200249{
250 struct ad198x_spec *spec = codec->spec;
Takashi Iwai9a081602008-02-12 18:37:26 +0100251 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
252 hinfo);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200253}
254
255static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
256 struct hda_codec *codec,
257 unsigned int stream_tag,
258 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100259 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200260{
261 struct ad198x_spec *spec = codec->spec;
262 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
263 format, substream);
264}
265
266static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
267 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100268 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200269{
270 struct ad198x_spec *spec = codec->spec;
271 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
272}
273
274/*
275 * Digital out
276 */
277static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
278 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100279 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200280{
281 struct ad198x_spec *spec = codec->spec;
282 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
283}
284
285static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
286 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100287 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200288{
289 struct ad198x_spec *spec = codec->spec;
290 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
291}
292
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200293static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
294 struct hda_codec *codec,
295 unsigned int stream_tag,
296 unsigned int format,
297 struct snd_pcm_substream *substream)
298{
299 struct ad198x_spec *spec = codec->spec;
300 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
301 format, substream);
302}
303
Takashi Iwai9411e212009-02-13 11:32:28 +0100304static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
305 struct hda_codec *codec,
306 struct snd_pcm_substream *substream)
307{
308 struct ad198x_spec *spec = codec->spec;
309 return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
310}
311
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200312/*
313 * Analog capture
314 */
315static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
316 struct hda_codec *codec,
317 unsigned int stream_tag,
318 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100319 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200320{
321 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100322 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
323 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200324 return 0;
325}
326
327static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
328 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100329 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200330{
331 struct ad198x_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +0100332 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200333 return 0;
334}
335
336
337/*
338 */
339static struct hda_pcm_stream ad198x_pcm_analog_playback = {
340 .substreams = 1,
341 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100342 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200343 .nid = 0, /* fill later */
344 .ops = {
345 .open = ad198x_playback_pcm_open,
346 .prepare = ad198x_playback_pcm_prepare,
347 .cleanup = ad198x_playback_pcm_cleanup
348 },
349};
350
351static struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100352 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200353 .channels_min = 2,
354 .channels_max = 2,
355 .nid = 0, /* fill later */
356 .ops = {
357 .prepare = ad198x_capture_pcm_prepare,
358 .cleanup = ad198x_capture_pcm_cleanup
359 },
360};
361
362static struct hda_pcm_stream ad198x_pcm_digital_playback = {
363 .substreams = 1,
364 .channels_min = 2,
365 .channels_max = 2,
366 .nid = 0, /* fill later */
367 .ops = {
368 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200369 .close = ad198x_dig_playback_pcm_close,
Takashi Iwai9411e212009-02-13 11:32:28 +0100370 .prepare = ad198x_dig_playback_pcm_prepare,
371 .cleanup = ad198x_dig_playback_pcm_cleanup
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200372 },
373};
374
Takashi Iwai985be542005-11-02 18:26:49 +0100375static struct hda_pcm_stream ad198x_pcm_digital_capture = {
376 .substreams = 1,
377 .channels_min = 2,
378 .channels_max = 2,
379 /* NID is set in alc_build_pcms */
380};
381
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200382static int ad198x_build_pcms(struct hda_codec *codec)
383{
384 struct ad198x_spec *spec = codec->spec;
385 struct hda_pcm *info = spec->pcm_rec;
386
387 codec->num_pcms = 1;
388 codec->pcm_info = info;
389
390 info->name = "AD198x Analog";
391 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
392 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
393 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
394 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100395 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
396 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200397
398 if (spec->multiout.dig_out_nid) {
399 info++;
400 codec->num_pcms++;
401 info->name = "AD198x Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +0100402 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200403 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
404 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100405 if (spec->dig_in_nid) {
406 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
407 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
408 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200409 }
410
411 return 0;
412}
413
Takashi Iwai603c4012008-07-30 15:01:44 +0200414static void ad198x_free_kctls(struct hda_codec *codec)
415{
416 struct ad198x_spec *spec = codec->spec;
417
418 if (spec->kctls.list) {
419 struct snd_kcontrol_new *kctl = spec->kctls.list;
420 int i;
421 for (i = 0; i < spec->kctls.used; i++)
422 kfree(kctl[i].name);
423 }
424 snd_array_free(&spec->kctls);
425}
426
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200427static void ad198x_free(struct hda_codec *codec)
428{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100429 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +0100430
Takashi Iwai603c4012008-07-30 15:01:44 +0200431 if (!spec)
432 return;
433
434 ad198x_free_kctls(codec);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100435 kfree(spec);
436 snd_hda_detach_beep_device(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200437}
438
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200439static struct hda_codec_ops ad198x_patch_ops = {
440 .build_controls = ad198x_build_controls,
441 .build_pcms = ad198x_build_pcms,
442 .init = ad198x_init,
443 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200444#ifdef CONFIG_SND_HDA_POWER_SAVE
445 .check_power_status = ad198x_check_power_status,
446#endif
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200447};
448
449
450/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100451 * EAPD control
452 * the private value = nid | (invert << 8)
453 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200454#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100455
456static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
457 struct snd_ctl_elem_value *ucontrol)
458{
459 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
460 struct ad198x_spec *spec = codec->spec;
461 int invert = (kcontrol->private_value >> 8) & 1;
462 if (invert)
463 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
464 else
465 ucontrol->value.integer.value[0] = spec->cur_eapd;
466 return 0;
467}
468
469static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
470 struct snd_ctl_elem_value *ucontrol)
471{
472 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
473 struct ad198x_spec *spec = codec->spec;
474 int invert = (kcontrol->private_value >> 8) & 1;
475 hda_nid_t nid = kcontrol->private_value & 0xff;
476 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100477 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwai18a815d2006-03-01 19:54:39 +0100478 if (invert)
479 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200480 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100481 return 0;
482 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200483 snd_hda_codec_write_cache(codec, nid,
484 0, AC_VERB_SET_EAPD_BTLENABLE,
485 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100486 return 1;
487}
488
Takashi Iwai9230d212006-03-13 13:49:49 +0100489static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
490 struct snd_ctl_elem_info *uinfo);
491static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
492 struct snd_ctl_elem_value *ucontrol);
493static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
494 struct snd_ctl_elem_value *ucontrol);
495
496
Takashi Iwai18a815d2006-03-01 19:54:39 +0100497/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200498 * AD1986A specific
499 */
500
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501#define AD1986A_SPDIF_OUT 0x02
502#define AD1986A_FRONT_DAC 0x03
503#define AD1986A_SURR_DAC 0x04
504#define AD1986A_CLFE_DAC 0x05
505#define AD1986A_ADC 0x06
506
507static hda_nid_t ad1986a_dac_nids[3] = {
508 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
509};
Takashi Iwai985be542005-11-02 18:26:49 +0100510static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +0100511static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
513static struct hda_input_mux ad1986a_capture_source = {
514 .num_items = 7,
515 .items = {
516 { "Mic", 0x0 },
517 { "CD", 0x1 },
518 { "Aux", 0x3 },
519 { "Line", 0x4 },
520 { "Mix", 0x5 },
521 { "Mono", 0x6 },
522 { "Phone", 0x7 },
523 },
524};
525
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526
Takashi Iwai532d5382007-07-27 19:02:40 +0200527static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
528 .ops = &snd_hda_bind_vol,
529 .values = {
530 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
531 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
532 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
533 0
534 },
535};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
Takashi Iwai532d5382007-07-27 19:02:40 +0200537static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
538 .ops = &snd_hda_bind_sw,
539 .values = {
540 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
541 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
542 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
543 0
544 },
545};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546
547/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 * mixers
549 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100550static struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200551 /*
552 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
553 */
554 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
555 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
557 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
558 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
559 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
560 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
561 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
562 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
563 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
564 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
565 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
566 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
567 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
568 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
569 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
570 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
571 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
572 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
573 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100574 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
576 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
577 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
578 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
579 {
580 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
581 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200582 .info = ad198x_mux_enum_info,
583 .get = ad198x_mux_enum_get,
584 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 },
586 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
587 { } /* end */
588};
589
Takashi Iwai9230d212006-03-13 13:49:49 +0100590/* additional mixers for 3stack mode */
591static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
592 {
593 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
594 .name = "Channel Mode",
595 .info = ad198x_ch_mode_info,
596 .get = ad198x_ch_mode_get,
597 .put = ad198x_ch_mode_put,
598 },
599 { } /* end */
600};
601
602/* laptop model - 2ch only */
603static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
604
Takashi Iwai20a45e82007-08-15 22:20:45 +0200605/* master controls both pins 0x1a and 0x1b */
606static struct hda_bind_ctls ad1986a_laptop_master_vol = {
607 .ops = &snd_hda_bind_vol,
608 .values = {
609 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
610 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
611 0,
612 },
613};
614
615static struct hda_bind_ctls ad1986a_laptop_master_sw = {
616 .ops = &snd_hda_bind_sw,
617 .values = {
618 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
619 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
620 0,
621 },
622};
623
Takashi Iwai9230d212006-03-13 13:49:49 +0100624static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
625 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
626 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200627 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
628 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100629 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
630 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
631 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
632 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
633 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
634 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
635 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
636 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100637 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100638 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100639 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
640 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
641 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
642 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
643 {
644 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
645 .name = "Capture Source",
646 .info = ad198x_mux_enum_info,
647 .get = ad198x_mux_enum_get,
648 .put = ad198x_mux_enum_put,
649 },
650 { } /* end */
651};
652
Takashi Iwai825aa972006-03-17 10:50:49 +0100653/* laptop-eapd model - 2ch only */
654
Takashi Iwai825aa972006-03-17 10:50:49 +0100655static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
656 .num_items = 3,
657 .items = {
658 { "Mic", 0x0 },
659 { "Internal Mic", 0x4 },
660 { "Mix", 0x5 },
661 },
662};
663
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100664static struct hda_input_mux ad1986a_automic_capture_source = {
665 .num_items = 2,
666 .items = {
667 { "Mic", 0x0 },
668 { "Mix", 0x5 },
669 },
670};
671
Takashi Iwai825aa972006-03-17 10:50:49 +0100672static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200673 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
674 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai825aa972006-03-17 10:50:49 +0100675 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
676 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100677 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
678 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
679 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
680 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
681 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
682 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
683 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
684 {
685 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
686 .name = "Capture Source",
687 .info = ad198x_mux_enum_info,
688 .get = ad198x_mux_enum_get,
689 .put = ad198x_mux_enum_put,
690 },
691 {
692 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
693 .name = "External Amplifier",
694 .info = ad198x_eapd_info,
695 .get = ad198x_eapd_get,
696 .put = ad198x_eapd_put,
697 .private_value = 0x1b | (1 << 8), /* port-D, inversed */
698 },
699 { } /* end */
700};
701
702static struct snd_kcontrol_new ad1986a_samsung_mixers[] = {
703 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
704 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
705 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
706 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai825aa972006-03-17 10:50:49 +0100707 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
708 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100709 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai825aa972006-03-17 10:50:49 +0100710 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
711 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
712 {
713 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
714 .name = "Capture Source",
715 .info = ad198x_mux_enum_info,
716 .get = ad198x_mux_enum_get,
717 .put = ad198x_mux_enum_put,
718 },
719 {
720 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
721 .name = "External Amplifier",
722 .info = ad198x_eapd_info,
723 .get = ad198x_eapd_get,
724 .put = ad198x_eapd_put,
725 .private_value = 0x1b | (1 << 8), /* port-D, inversed */
726 },
727 { } /* end */
728};
729
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100730/* re-connect the mic boost input according to the jack sensing */
731static void ad1986a_automic(struct hda_codec *codec)
732{
733 unsigned int present;
734 present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0);
735 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
736 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
737 (present & AC_PINSENSE_PRESENCE) ? 0 : 2);
738}
739
740#define AD1986A_MIC_EVENT 0x36
741
742static void ad1986a_automic_unsol_event(struct hda_codec *codec,
743 unsigned int res)
744{
745 if ((res >> 26) != AD1986A_MIC_EVENT)
746 return;
747 ad1986a_automic(codec);
748}
749
750static int ad1986a_automic_init(struct hda_codec *codec)
751{
752 ad198x_init(codec);
753 ad1986a_automic(codec);
754 return 0;
755}
756
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200757/* laptop-automute - 2ch only */
758
759static void ad1986a_update_hp(struct hda_codec *codec)
760{
761 struct ad198x_spec *spec = codec->spec;
762 unsigned int mute;
763
764 if (spec->jack_present)
765 mute = HDA_AMP_MUTE; /* mute internal speaker */
766 else
767 /* unmute internal speaker if necessary */
768 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
769 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
770 HDA_AMP_MUTE, mute);
771}
772
773static void ad1986a_hp_automute(struct hda_codec *codec)
774{
775 struct ad198x_spec *spec = codec->spec;
776 unsigned int present;
777
778 present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0);
Takashi Iwai53eb1b82007-10-17 10:09:32 +0200779 /* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
780 spec->jack_present = !(present & 0x80000000);
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200781 ad1986a_update_hp(codec);
782}
783
784#define AD1986A_HP_EVENT 0x37
785
786static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
787{
788 if ((res >> 26) != AD1986A_HP_EVENT)
789 return;
790 ad1986a_hp_automute(codec);
791}
792
793static int ad1986a_hp_init(struct hda_codec *codec)
794{
795 ad198x_init(codec);
796 ad1986a_hp_automute(codec);
797 return 0;
798}
799
800/* bind hp and internal speaker mute (with plug check) */
801static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
802 struct snd_ctl_elem_value *ucontrol)
803{
804 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
805 long *valp = ucontrol->value.integer.value;
806 int change;
807
808 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
809 HDA_AMP_MUTE,
810 valp[0] ? 0 : HDA_AMP_MUTE);
811 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
812 HDA_AMP_MUTE,
813 valp[1] ? 0 : HDA_AMP_MUTE);
814 if (change)
815 ad1986a_update_hp(codec);
816 return change;
817}
818
819static struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = {
820 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
821 {
822 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
823 .name = "Master Playback Switch",
824 .info = snd_hda_mixer_amp_switch_info,
825 .get = snd_hda_mixer_amp_switch_get,
826 .put = ad1986a_hp_master_sw_put,
827 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
828 },
829 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
830 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
831 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT),
832 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT),
833 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
834 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
835 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200836 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
837 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
838 {
839 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
840 .name = "Capture Source",
841 .info = ad198x_mux_enum_info,
842 .get = ad198x_mux_enum_get,
843 .put = ad198x_mux_enum_put,
844 },
845 {
846 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
847 .name = "External Amplifier",
848 .info = ad198x_eapd_info,
849 .get = ad198x_eapd_get,
850 .put = ad198x_eapd_put,
851 .private_value = 0x1b | (1 << 8), /* port-D, inversed */
852 },
853 { } /* end */
854};
855
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856/*
857 * initialization verbs
858 */
859static struct hda_verb ad1986a_init_verbs[] = {
860 /* Front, Surround, CLFE DAC; mute as default */
861 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
862 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
863 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
864 /* Downmix - off */
865 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
866 /* HP, Line-Out, Surround, CLFE selectors */
867 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
868 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
869 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
870 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
871 /* Mono selector */
872 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
873 /* Mic selector: Mic 1/2 pin */
874 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
875 /* Line-in selector: Line-in */
876 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
877 /* Mic 1/2 swap */
878 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
879 /* Record selector: mic */
880 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
881 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
882 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
883 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
884 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
885 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
886 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
887 /* PC beep */
888 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
889 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
890 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
891 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
892 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
893 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
894 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200895 /* HP Pin */
896 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
897 /* Front, Surround, CLFE Pins */
898 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
899 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
900 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
901 /* Mono Pin */
902 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
903 /* Mic Pin */
904 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
905 /* Line, Aux, CD, Beep-In Pin */
906 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
907 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
908 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
909 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
910 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 { } /* end */
912};
913
Takashi Iwai9230d212006-03-13 13:49:49 +0100914static struct hda_verb ad1986a_ch2_init[] = {
915 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200916 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
917 /* Line-in selectors */
918 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100919 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200920 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
921 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
922 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100923 { } /* end */
924};
925
926static struct hda_verb ad1986a_ch4_init[] = {
927 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200928 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
929 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100930 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200931 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
932 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100933 { } /* end */
934};
935
936static struct hda_verb ad1986a_ch6_init[] = {
937 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200938 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
939 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100940 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200941 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
942 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100943 { } /* end */
944};
945
946static struct hda_channel_mode ad1986a_modes[3] = {
947 { 2, ad1986a_ch2_init },
948 { 4, ad1986a_ch4_init },
949 { 6, ad1986a_ch6_init },
950};
951
Takashi Iwai825aa972006-03-17 10:50:49 +0100952/* eapd initialization */
953static struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +0100954 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa972006-03-17 10:50:49 +0100955 {}
956};
957
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100958static struct hda_verb ad1986a_automic_verbs[] = {
959 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
960 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
961 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
962 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
963 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
964 {}
965};
966
Tobin Davisf36090f2007-01-08 11:07:12 +0100967/* Ultra initialization */
968static struct hda_verb ad1986a_ultra_init[] = {
969 /* eapd initialization */
970 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
971 /* CLFE -> Mic in */
972 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
973 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
974 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
975 { } /* end */
976};
977
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200978/* pin sensing on HP jack */
979static struct hda_verb ad1986a_hp_init_verbs[] = {
980 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
981 {}
982};
983
984
Takashi Iwai9230d212006-03-13 13:49:49 +0100985/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100986enum {
987 AD1986A_6STACK,
988 AD1986A_3STACK,
989 AD1986A_LAPTOP,
990 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200991 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +0100992 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +0100993 AD1986A_SAMSUNG,
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100994 AD1986A_MODELS
995};
Takashi Iwai9230d212006-03-13 13:49:49 +0100996
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100997static const char *ad1986a_models[AD1986A_MODELS] = {
998 [AD1986A_6STACK] = "6stack",
999 [AD1986A_3STACK] = "3stack",
1000 [AD1986A_LAPTOP] = "laptop",
1001 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001002 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +01001003 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +01001004 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001005};
1006
1007static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
1008 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001009 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001010 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001011 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001012 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1013 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1014 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1015 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001016 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001017 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001018 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1019 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1020 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1021 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1022 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001023 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Takashi Iwai7db756f2007-12-24 14:36:09 +01001024 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
Tobin Davis18768992007-03-12 22:20:51 +01001025 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001026 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Tobin Davisf36090f2007-01-08 11:07:12 +01001027 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001028 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001029 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001030 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001031 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001032 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001033 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001034 {}
1035};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036
Takashi Iwaicb53c622007-08-10 17:21:45 +02001037#ifdef CONFIG_SND_HDA_POWER_SAVE
1038static struct hda_amp_list ad1986a_loopbacks[] = {
1039 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1040 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1041 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1042 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1043 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1044 { } /* end */
1045};
1046#endif
1047
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001048static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1049{
1050 unsigned int conf = snd_hda_codec_read(codec, nid, 0,
1051 AC_VERB_GET_CONFIG_DEFAULT, 0);
1052 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1053}
1054
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055static int patch_ad1986a(struct hda_codec *codec)
1056{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001057 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001058 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001060 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 if (spec == NULL)
1062 return -ENOMEM;
1063
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 codec->spec = spec;
1065
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001066 err = snd_hda_attach_beep_device(codec, 0x19);
1067 if (err < 0) {
1068 ad198x_free(codec);
1069 return err;
1070 }
1071 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1072
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 spec->multiout.max_channels = 6;
1074 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1075 spec->multiout.dac_nids = ad1986a_dac_nids;
1076 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001077 spec->num_adc_nids = 1;
1078 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001079 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001080 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001081 spec->num_mixers = 1;
1082 spec->mixers[0] = ad1986a_mixers;
1083 spec->num_init_verbs = 1;
1084 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001085#ifdef CONFIG_SND_HDA_POWER_SAVE
1086 spec->loopback.amplist = ad1986a_loopbacks;
1087#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001088 spec->vmaster_nid = 0x1b;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001090 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091
Takashi Iwai9230d212006-03-13 13:49:49 +01001092 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001093 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1094 ad1986a_models,
1095 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001096 switch (board_config) {
1097 case AD1986A_3STACK:
1098 spec->num_mixers = 2;
1099 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001100 spec->num_init_verbs = 2;
1101 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001102 spec->channel_mode = ad1986a_modes;
1103 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001104 spec->need_dac_fix = 1;
1105 spec->multiout.max_channels = 2;
1106 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001107 break;
1108 case AD1986A_LAPTOP:
1109 spec->mixers[0] = ad1986a_laptop_mixers;
1110 spec->multiout.max_channels = 2;
1111 spec->multiout.num_dacs = 1;
1112 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1113 break;
Takashi Iwai825aa972006-03-17 10:50:49 +01001114 case AD1986A_LAPTOP_EAPD:
1115 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001116 spec->num_init_verbs = 2;
1117 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1118 spec->multiout.max_channels = 2;
1119 spec->multiout.num_dacs = 1;
1120 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1121 if (!is_jack_available(codec, 0x25))
1122 spec->multiout.dig_out_nid = 0;
1123 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1124 break;
1125 case AD1986A_SAMSUNG:
1126 spec->mixers[0] = ad1986a_samsung_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001127 spec->num_init_verbs = 3;
Takashi Iwai825aa972006-03-17 10:50:49 +01001128 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001129 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa972006-03-17 10:50:49 +01001130 spec->multiout.max_channels = 2;
1131 spec->multiout.num_dacs = 1;
1132 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001133 if (!is_jack_available(codec, 0x25))
1134 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001135 spec->input_mux = &ad1986a_automic_capture_source;
1136 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1137 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa972006-03-17 10:50:49 +01001138 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001139 case AD1986A_LAPTOP_AUTOMUTE:
1140 spec->mixers[0] = ad1986a_laptop_automute_mixers;
1141 spec->num_init_verbs = 3;
1142 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1143 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1144 spec->multiout.max_channels = 2;
1145 spec->multiout.num_dacs = 1;
1146 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001147 if (!is_jack_available(codec, 0x25))
1148 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001149 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1150 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1151 codec->patch_ops.init = ad1986a_hp_init;
1152 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001153 case AD1986A_ULTRA:
1154 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1155 spec->num_init_verbs = 2;
1156 spec->init_verbs[1] = ad1986a_ultra_init;
1157 spec->multiout.max_channels = 2;
1158 spec->multiout.num_dacs = 1;
1159 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1160 spec->multiout.dig_out_nid = 0;
1161 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001162 }
1163
Takashi Iwaid29240c2007-10-26 12:35:56 +02001164 /* AD1986A has a hardware problem that it can't share a stream
1165 * with multiple output pins. The copy of front to surrounds
1166 * causes noisy or silent outputs at a certain timing, e.g.
1167 * changing the volume.
1168 * So, let's disable the shared stream.
1169 */
1170 spec->multiout.no_share_stream = 1;
1171
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 return 0;
1173}
1174
1175/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001176 * AD1983 specific
1177 */
1178
1179#define AD1983_SPDIF_OUT 0x02
1180#define AD1983_DAC 0x03
1181#define AD1983_ADC 0x04
1182
1183static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001184static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001185static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001186
1187static struct hda_input_mux ad1983_capture_source = {
1188 .num_items = 4,
1189 .items = {
1190 { "Mic", 0x0 },
1191 { "Line", 0x1 },
1192 { "Mix", 0x2 },
1193 { "Mix Mono", 0x3 },
1194 },
1195};
1196
1197/*
1198 * SPDIF playback route
1199 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001200static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001201{
1202 static char *texts[] = { "PCM", "ADC" };
1203
1204 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1205 uinfo->count = 1;
1206 uinfo->value.enumerated.items = 2;
1207 if (uinfo->value.enumerated.item > 1)
1208 uinfo->value.enumerated.item = 1;
1209 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1210 return 0;
1211}
1212
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001213static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001214{
1215 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1216 struct ad198x_spec *spec = codec->spec;
1217
1218 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1219 return 0;
1220}
1221
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001222static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001223{
1224 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1225 struct ad198x_spec *spec = codec->spec;
1226
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001227 if (ucontrol->value.enumerated.item[0] > 1)
1228 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001229 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1230 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001231 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1232 AC_VERB_SET_CONNECT_SEL,
1233 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001234 return 1;
1235 }
1236 return 0;
1237}
1238
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001239static struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001240 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1241 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1242 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1243 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1244 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1245 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1246 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1247 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1248 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1249 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1250 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1251 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001252 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
1253 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1254 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1255 {
1256 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1257 .name = "Capture Source",
1258 .info = ad198x_mux_enum_info,
1259 .get = ad198x_mux_enum_get,
1260 .put = ad198x_mux_enum_put,
1261 },
1262 {
1263 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001264 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001265 .info = ad1983_spdif_route_info,
1266 .get = ad1983_spdif_route_get,
1267 .put = ad1983_spdif_route_put,
1268 },
1269 { } /* end */
1270};
1271
1272static struct hda_verb ad1983_init_verbs[] = {
1273 /* Front, HP, Mono; mute as default */
1274 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1275 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1276 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1277 /* Beep, PCM, Mic, Line-In: mute */
1278 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1279 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1280 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1281 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1282 /* Front, HP selectors; from Mix */
1283 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1284 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1285 /* Mono selector; from Mix */
1286 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1287 /* Mic selector; Mic */
1288 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1289 /* Line-in selector: Line-in */
1290 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1291 /* Mic boost: 0dB */
1292 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1293 /* Record selector: mic */
1294 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1295 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1296 /* SPDIF route: PCM */
1297 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1298 /* Front Pin */
1299 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1300 /* HP Pin */
1301 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1302 /* Mono Pin */
1303 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1304 /* Mic Pin */
1305 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1306 /* Line Pin */
1307 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1308 { } /* end */
1309};
1310
Takashi Iwaicb53c622007-08-10 17:21:45 +02001311#ifdef CONFIG_SND_HDA_POWER_SAVE
1312static struct hda_amp_list ad1983_loopbacks[] = {
1313 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1314 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1315 { } /* end */
1316};
1317#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001318
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001319static int patch_ad1983(struct hda_codec *codec)
1320{
1321 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001322 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001323
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001324 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001325 if (spec == NULL)
1326 return -ENOMEM;
1327
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001328 codec->spec = spec;
1329
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001330 err = snd_hda_attach_beep_device(codec, 0x10);
1331 if (err < 0) {
1332 ad198x_free(codec);
1333 return err;
1334 }
1335 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1336
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001337 spec->multiout.max_channels = 2;
1338 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1339 spec->multiout.dac_nids = ad1983_dac_nids;
1340 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001341 spec->num_adc_nids = 1;
1342 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001343 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001344 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001345 spec->num_mixers = 1;
1346 spec->mixers[0] = ad1983_mixers;
1347 spec->num_init_verbs = 1;
1348 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001349 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001350#ifdef CONFIG_SND_HDA_POWER_SAVE
1351 spec->loopback.amplist = ad1983_loopbacks;
1352#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001353 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001354
1355 codec->patch_ops = ad198x_patch_ops;
1356
1357 return 0;
1358}
1359
1360
1361/*
1362 * AD1981 HD specific
1363 */
1364
1365#define AD1981_SPDIF_OUT 0x02
1366#define AD1981_DAC 0x03
1367#define AD1981_ADC 0x04
1368
1369static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001370static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001371static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001372
1373/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
1374static struct hda_input_mux ad1981_capture_source = {
1375 .num_items = 7,
1376 .items = {
1377 { "Front Mic", 0x0 },
1378 { "Line", 0x1 },
1379 { "Mix", 0x2 },
1380 { "Mix Mono", 0x3 },
1381 { "CD", 0x4 },
1382 { "Mic", 0x6 },
1383 { "Aux", 0x7 },
1384 },
1385};
1386
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001387static struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001388 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1389 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1390 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1391 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1392 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1393 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1394 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1395 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1396 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1397 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1398 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1399 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1400 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1401 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1402 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1403 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1404 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1405 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001406 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
1407 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
1408 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1409 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1410 {
1411 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1412 .name = "Capture Source",
1413 .info = ad198x_mux_enum_info,
1414 .get = ad198x_mux_enum_get,
1415 .put = ad198x_mux_enum_put,
1416 },
1417 /* identical with AD1983 */
1418 {
1419 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001420 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001421 .info = ad1983_spdif_route_info,
1422 .get = ad1983_spdif_route_get,
1423 .put = ad1983_spdif_route_put,
1424 },
1425 { } /* end */
1426};
1427
1428static struct hda_verb ad1981_init_verbs[] = {
1429 /* Front, HP, Mono; mute as default */
1430 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1431 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1432 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1433 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1434 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1435 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1436 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1437 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1438 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1439 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1440 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1441 /* Front, HP selectors; from Mix */
1442 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1443 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1444 /* Mono selector; from Mix */
1445 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1446 /* Mic Mixer; select Front Mic */
1447 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1448 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1449 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001450 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1451 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001452 /* Record selector: Front mic */
1453 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1454 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1455 /* SPDIF route: PCM */
1456 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1457 /* Front Pin */
1458 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1459 /* HP Pin */
1460 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1461 /* Mono Pin */
1462 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1463 /* Front & Rear Mic Pins */
1464 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1465 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1466 /* Line Pin */
1467 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1468 /* Digital Beep */
1469 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1470 /* Line-Out as Input: disabled */
1471 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1472 { } /* end */
1473};
1474
Takashi Iwaicb53c622007-08-10 17:21:45 +02001475#ifdef CONFIG_SND_HDA_POWER_SAVE
1476static struct hda_amp_list ad1981_loopbacks[] = {
1477 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1478 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1479 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1480 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1481 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1482 { } /* end */
1483};
1484#endif
1485
Takashi Iwai18a815d2006-03-01 19:54:39 +01001486/*
1487 * Patch for HP nx6320
1488 *
Tobin Davis18768992007-03-12 22:20:51 +01001489 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001490 * speaker output enabled _and_ mute-LED off.
1491 */
1492
1493#define AD1981_HP_EVENT 0x37
1494#define AD1981_MIC_EVENT 0x38
1495
1496static struct hda_verb ad1981_hp_init_verbs[] = {
1497 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1498 /* pin sensing on HP and Mic jacks */
1499 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1500 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1501 {}
1502};
1503
1504/* turn on/off EAPD (+ mute HP) as a master switch */
1505static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1506 struct snd_ctl_elem_value *ucontrol)
1507{
1508 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1509 struct ad198x_spec *spec = codec->spec;
1510
1511 if (! ad198x_eapd_put(kcontrol, ucontrol))
1512 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001513 /* change speaker pin appropriately */
1514 snd_hda_codec_write(codec, 0x05, 0,
1515 AC_VERB_SET_PIN_WIDGET_CONTROL,
1516 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001517 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001518 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1519 HDA_AMP_MUTE,
1520 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001521 return 1;
1522}
1523
1524/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02001525static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
1526 .ops = &snd_hda_bind_vol,
1527 .values = {
1528 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1529 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1530 0
1531 },
1532};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001533
1534/* mute internal speaker if HP is plugged */
1535static void ad1981_hp_automute(struct hda_codec *codec)
1536{
1537 unsigned int present;
1538
1539 present = snd_hda_codec_read(codec, 0x06, 0,
1540 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
Takashi Iwai47fd8302007-08-10 17:11:07 +02001541 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1542 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001543}
1544
1545/* toggle input of built-in and mic jack appropriately */
1546static void ad1981_hp_automic(struct hda_codec *codec)
1547{
1548 static struct hda_verb mic_jack_on[] = {
1549 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1550 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1551 {}
1552 };
1553 static struct hda_verb mic_jack_off[] = {
1554 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1555 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1556 {}
1557 };
1558 unsigned int present;
1559
1560 present = snd_hda_codec_read(codec, 0x08, 0,
1561 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1562 if (present)
1563 snd_hda_sequence_write(codec, mic_jack_on);
1564 else
1565 snd_hda_sequence_write(codec, mic_jack_off);
1566}
1567
1568/* unsolicited event for HP jack sensing */
1569static void ad1981_hp_unsol_event(struct hda_codec *codec,
1570 unsigned int res)
1571{
1572 res >>= 26;
1573 switch (res) {
1574 case AD1981_HP_EVENT:
1575 ad1981_hp_automute(codec);
1576 break;
1577 case AD1981_MIC_EVENT:
1578 ad1981_hp_automic(codec);
1579 break;
1580 }
1581}
1582
1583static struct hda_input_mux ad1981_hp_capture_source = {
1584 .num_items = 3,
1585 .items = {
1586 { "Mic", 0x0 },
1587 { "Docking-Station", 0x1 },
1588 { "Mix", 0x2 },
1589 },
1590};
1591
1592static struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001593 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001594 {
1595 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1596 .name = "Master Playback Switch",
1597 .info = ad198x_eapd_info,
1598 .get = ad198x_eapd_get,
1599 .put = ad1981_hp_master_sw_put,
1600 .private_value = 0x05,
1601 },
1602 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1603 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1604#if 0
1605 /* FIXME: analog mic/line loopback doesn't work with my tests...
1606 * (although recording is OK)
1607 */
1608 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1609 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1610 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1611 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1612 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1613 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1614 /* FIXME: does this laptop have analog CD connection? */
1615 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1616 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1617#endif
1618 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1619 HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
1620 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1621 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1622 {
1623 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1624 .name = "Capture Source",
1625 .info = ad198x_mux_enum_info,
1626 .get = ad198x_mux_enum_get,
1627 .put = ad198x_mux_enum_put,
1628 },
1629 { } /* end */
1630};
1631
1632/* initialize jack-sensing, too */
1633static int ad1981_hp_init(struct hda_codec *codec)
1634{
1635 ad198x_init(codec);
1636 ad1981_hp_automute(codec);
1637 ad1981_hp_automic(codec);
1638 return 0;
1639}
1640
Tobin Davis18768992007-03-12 22:20:51 +01001641/* configuration for Toshiba Laptops */
1642static struct hda_verb ad1981_toshiba_init_verbs[] = {
1643 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1644 /* pin sensing on HP and Mic jacks */
1645 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1646 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1647 {}
1648};
1649
1650static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
1651 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1652 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1653 { }
1654};
1655
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001656/* configuration for Lenovo Thinkpad T60 */
1657static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
1658 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1659 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1660 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1661 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1662 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1663 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1664 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1665 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1666 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1667 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1668 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1669 {
1670 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1671 .name = "Capture Source",
1672 .info = ad198x_mux_enum_info,
1673 .get = ad198x_mux_enum_get,
1674 .put = ad198x_mux_enum_put,
1675 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001676 /* identical with AD1983 */
1677 {
1678 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1679 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1680 .info = ad1983_spdif_route_info,
1681 .get = ad1983_spdif_route_get,
1682 .put = ad1983_spdif_route_put,
1683 },
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001684 { } /* end */
1685};
1686
1687static struct hda_input_mux ad1981_thinkpad_capture_source = {
1688 .num_items = 3,
1689 .items = {
1690 { "Mic", 0x0 },
1691 { "Mix", 0x2 },
1692 { "CD", 0x4 },
1693 },
1694};
1695
Takashi Iwai18a815d2006-03-01 19:54:39 +01001696/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001697enum {
1698 AD1981_BASIC,
1699 AD1981_HP,
1700 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001701 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001702 AD1981_MODELS
1703};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001704
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001705static const char *ad1981_models[AD1981_MODELS] = {
1706 [AD1981_HP] = "hp",
1707 [AD1981_THINKPAD] = "thinkpad",
1708 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001709 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001710};
1711
1712static struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001713 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001714 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001715 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001716 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001717 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001718 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001719 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001720 /* HP nx6320 (reversed SSID, H/W bug) */
1721 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001722 {}
1723};
1724
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001725static int patch_ad1981(struct hda_codec *codec)
1726{
1727 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001728 int err, board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001729
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001730 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001731 if (spec == NULL)
1732 return -ENOMEM;
1733
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001734 codec->spec = spec;
1735
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001736 err = snd_hda_attach_beep_device(codec, 0x10);
1737 if (err < 0) {
1738 ad198x_free(codec);
1739 return err;
1740 }
1741 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
1742
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001743 spec->multiout.max_channels = 2;
1744 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1745 spec->multiout.dac_nids = ad1981_dac_nids;
1746 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001747 spec->num_adc_nids = 1;
1748 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001749 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001750 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001751 spec->num_mixers = 1;
1752 spec->mixers[0] = ad1981_mixers;
1753 spec->num_init_verbs = 1;
1754 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001755 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001756#ifdef CONFIG_SND_HDA_POWER_SAVE
1757 spec->loopback.amplist = ad1981_loopbacks;
1758#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001759 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001760
1761 codec->patch_ops = ad198x_patch_ops;
1762
Takashi Iwai18a815d2006-03-01 19:54:39 +01001763 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001764 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1765 ad1981_models,
1766 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001767 switch (board_config) {
1768 case AD1981_HP:
1769 spec->mixers[0] = ad1981_hp_mixers;
1770 spec->num_init_verbs = 2;
1771 spec->init_verbs[1] = ad1981_hp_init_verbs;
1772 spec->multiout.dig_out_nid = 0;
1773 spec->input_mux = &ad1981_hp_capture_source;
1774
1775 codec->patch_ops.init = ad1981_hp_init;
1776 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1777 break;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001778 case AD1981_THINKPAD:
1779 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001780 spec->input_mux = &ad1981_thinkpad_capture_source;
1781 break;
Tobin Davis18768992007-03-12 22:20:51 +01001782 case AD1981_TOSHIBA:
1783 spec->mixers[0] = ad1981_hp_mixers;
1784 spec->mixers[1] = ad1981_toshiba_mixers;
1785 spec->num_init_verbs = 2;
1786 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1787 spec->multiout.dig_out_nid = 0;
1788 spec->input_mux = &ad1981_hp_capture_source;
1789 codec->patch_ops.init = ad1981_hp_init;
1790 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1791 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001792 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001793 return 0;
1794}
1795
1796
1797/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001798 * AD1988
1799 *
1800 * Output pins and routes
1801 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001802 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001803 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1804 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1805 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1806 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1807 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1808 * port-F 0x16 (mute) <- 0x2a <- 06
1809 * port-G 0x24 (mute) <- 0x27 <- 05
1810 * port-H 0x25 (mute) <- 0x28 <- 0a
1811 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1812 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001813 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1814 * (*) 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 +01001815 *
1816 * Input pins and routes
1817 *
1818 * pin boost mix input # / adc input #
1819 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1820 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1821 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1822 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1823 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1824 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1825 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1826 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1827 *
1828 *
1829 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001830 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001831 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001832 *
1833 * Inputs of Analog Mix (0x20)
1834 * 0:Port-B (front mic)
1835 * 1:Port-C/G/H (line-in)
1836 * 2:Port-A
1837 * 3:Port-D (line-in/2)
1838 * 4:Port-E/G/H (mic-in)
1839 * 5:Port-F (mic2-in)
1840 * 6:CD
1841 * 7:Beep
1842 *
1843 * ADC selection
1844 * 0:Port-A
1845 * 1:Port-B (front mic-in)
1846 * 2:Port-C (line-in)
1847 * 3:Port-F (mic2-in)
1848 * 4:Port-E (mic-in)
1849 * 5:CD
1850 * 6:Port-G
1851 * 7:Port-H
1852 * 8:Port-D (line-in/2)
1853 * 9:Mix
1854 *
1855 * Proposed pin assignments by the datasheet
1856 *
1857 * 6-stack
1858 * Port-A front headphone
1859 * B front mic-in
1860 * C rear line-in
1861 * D rear front-out
1862 * E rear mic-in
1863 * F rear surround
1864 * G rear CLFE
1865 * H rear side
1866 *
1867 * 3-stack
1868 * Port-A front headphone
1869 * B front mic
1870 * C rear line-in/surround
1871 * D rear front-out
1872 * E rear mic-in/CLFE
1873 *
1874 * laptop
1875 * Port-A headphone
1876 * B mic-in
1877 * C docking station
1878 * D internal speaker (with EAPD)
1879 * E/F quad mic array
1880 */
1881
1882
1883/* models */
1884enum {
1885 AD1988_6STACK,
1886 AD1988_6STACK_DIG,
1887 AD1988_3STACK,
1888 AD1988_3STACK_DIG,
1889 AD1988_LAPTOP,
1890 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01001891 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001892 AD1988_MODEL_LAST,
1893};
1894
Takashi Iwaid32410b12005-11-24 16:06:23 +01001895/* reivision id to check workarounds */
1896#define AD1988A_REV2 0x100200
1897
Takashi Iwai1a806f42006-07-03 15:58:16 +02001898#define is_rev2(codec) \
1899 ((codec)->vendor_id == 0x11d41988 && \
1900 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001901
1902/*
1903 * mixers
1904 */
1905
Takashi Iwaid32410b12005-11-24 16:06:23 +01001906static hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001907 0x04, 0x06, 0x05, 0x0a
1908};
1909
Takashi Iwaid32410b12005-11-24 16:06:23 +01001910static hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001911 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01001912};
1913
1914/* for AD1988A revision-2, DAC2-4 are swapped */
1915static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
1916 0x04, 0x05, 0x0a, 0x06
1917};
1918
1919static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001920 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01001921};
1922
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001923static hda_nid_t ad1988_adc_nids[3] = {
1924 0x08, 0x09, 0x0f
1925};
1926
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001927static hda_nid_t ad1988_capsrc_nids[3] = {
1928 0x0c, 0x0d, 0x0e
1929};
1930
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001931#define AD1988_SPDIF_OUT 0x02
1932#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001933#define AD1988_SPDIF_IN 0x07
1934
Takashi Iwai3a08e302009-02-13 11:37:08 +01001935static hda_nid_t ad1989b_slave_dig_outs[] = {
1936 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001937};
1938
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001939static struct hda_input_mux ad1988_6stack_capture_source = {
1940 .num_items = 5,
1941 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001942 { "Front Mic", 0x1 }, /* port-B */
1943 { "Line", 0x2 }, /* port-C */
1944 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001945 { "CD", 0x5 },
1946 { "Mix", 0x9 },
1947 },
1948};
1949
1950static struct hda_input_mux ad1988_laptop_capture_source = {
1951 .num_items = 3,
1952 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001953 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001954 { "CD", 0x5 },
1955 { "Mix", 0x9 },
1956 },
1957};
1958
1959/*
1960 */
1961static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
1962 struct snd_ctl_elem_info *uinfo)
1963{
1964 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1965 struct ad198x_spec *spec = codec->spec;
1966 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
1967 spec->num_channel_mode);
1968}
1969
1970static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
1971 struct snd_ctl_elem_value *ucontrol)
1972{
1973 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1974 struct ad198x_spec *spec = codec->spec;
1975 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
1976 spec->num_channel_mode, spec->multiout.max_channels);
1977}
1978
1979static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
1980 struct snd_ctl_elem_value *ucontrol)
1981{
1982 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1983 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001984 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
1985 spec->num_channel_mode,
1986 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02001987 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02001988 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001989 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001990}
1991
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001992/* 6-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001993static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001994 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1995 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1996 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
1997 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
1998 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001999 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002000};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002001
Takashi Iwaid32410b12005-11-24 16:06:23 +01002002static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
2003 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2004 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2005 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2006 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2007 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002008 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002009};
2010
2011static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002012 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2013 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2014 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2015 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2016 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2017 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2018 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2019
2020 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2021 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2022 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2023 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2024 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2025 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2026 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2027 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2028
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002029 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002030 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2031
2032 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2033 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2034
2035 { } /* end */
2036};
2037
2038/* 3-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002039static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002040 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002041 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002042 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2043 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002044 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002045};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002046
Takashi Iwaid32410b12005-11-24 16:06:23 +01002047static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
2048 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002049 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2050 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2051 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002052 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002053};
2054
2055static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002056 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002057 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2058 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2059 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002060 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2061 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2062
2063 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2064 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2065 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2066 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2067 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2068 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2069 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2070 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2071
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002072 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002073 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2074
2075 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2076 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2077 {
2078 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2079 .name = "Channel Mode",
2080 .info = ad198x_ch_mode_info,
2081 .get = ad198x_ch_mode_get,
2082 .put = ad198x_ch_mode_put,
2083 },
2084
2085 { } /* end */
2086};
2087
2088/* laptop mode */
2089static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
2090 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2091 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2092 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2093
2094 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2095 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2096 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2097 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2098 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2099 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2100
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002101 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002102 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2103
2104 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2105
2106 {
2107 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2108 .name = "External Amplifier",
Takashi Iwai18a815d2006-03-01 19:54:39 +01002109 .info = ad198x_eapd_info,
2110 .get = ad198x_eapd_get,
2111 .put = ad198x_eapd_put,
2112 .private_value = 0x12 | (1 << 8), /* port-D, inversed */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002113 },
2114
2115 { } /* end */
2116};
2117
2118/* capture */
2119static struct snd_kcontrol_new ad1988_capture_mixers[] = {
2120 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2121 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2122 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2123 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2124 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2125 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2126 {
2127 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2128 /* The multiple "Capture Source" controls confuse alsamixer
2129 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002130 */
2131 /* .name = "Capture Source", */
2132 .name = "Input Source",
2133 .count = 3,
2134 .info = ad198x_mux_enum_info,
2135 .get = ad198x_mux_enum_get,
2136 .put = ad198x_mux_enum_put,
2137 },
2138 { } /* end */
2139};
2140
2141static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2142 struct snd_ctl_elem_info *uinfo)
2143{
2144 static char *texts[] = {
2145 "PCM", "ADC1", "ADC2", "ADC3"
2146 };
2147 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2148 uinfo->count = 1;
2149 uinfo->value.enumerated.items = 4;
2150 if (uinfo->value.enumerated.item >= 4)
2151 uinfo->value.enumerated.item = 3;
2152 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2153 return 0;
2154}
2155
2156static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2157 struct snd_ctl_elem_value *ucontrol)
2158{
2159 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2160 unsigned int sel;
2161
Takashi Iwaibddcf542007-07-24 18:04:05 +02002162 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2163 AC_AMP_GET_INPUT);
2164 if (!(sel & 0x80))
2165 ucontrol->value.enumerated.item[0] = 0;
2166 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002167 sel = snd_hda_codec_read(codec, 0x0b, 0,
2168 AC_VERB_GET_CONNECT_SEL, 0);
2169 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002170 sel++;
2171 else
2172 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002173 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002174 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002175 return 0;
2176}
2177
2178static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2179 struct snd_ctl_elem_value *ucontrol)
2180{
2181 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002182 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002183 int change;
2184
Takashi Iwai35b26722007-05-05 12:17:17 +02002185 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002186 if (val > 3)
2187 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002188 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002189 sel = snd_hda_codec_read(codec, 0x1d, 0,
2190 AC_VERB_GET_AMP_GAIN_MUTE,
2191 AC_AMP_GET_INPUT);
2192 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002193 if (change) {
2194 snd_hda_codec_write_cache(codec, 0x1d, 0,
2195 AC_VERB_SET_AMP_GAIN_MUTE,
2196 AMP_IN_UNMUTE(0));
2197 snd_hda_codec_write_cache(codec, 0x1d, 0,
2198 AC_VERB_SET_AMP_GAIN_MUTE,
2199 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002200 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002201 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002202 sel = snd_hda_codec_read(codec, 0x1d, 0,
2203 AC_VERB_GET_AMP_GAIN_MUTE,
2204 AC_AMP_GET_INPUT | 0x01);
2205 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002206 if (change) {
2207 snd_hda_codec_write_cache(codec, 0x1d, 0,
2208 AC_VERB_SET_AMP_GAIN_MUTE,
2209 AMP_IN_MUTE(0));
2210 snd_hda_codec_write_cache(codec, 0x1d, 0,
2211 AC_VERB_SET_AMP_GAIN_MUTE,
2212 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002213 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002214 sel = snd_hda_codec_read(codec, 0x0b, 0,
2215 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2216 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002217 if (change)
2218 snd_hda_codec_write_cache(codec, 0x0b, 0,
2219 AC_VERB_SET_CONNECT_SEL,
2220 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002221 }
2222 return change;
2223}
2224
2225static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
2226 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2227 {
2228 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2229 .name = "IEC958 Playback Source",
2230 .info = ad1988_spdif_playback_source_info,
2231 .get = ad1988_spdif_playback_source_get,
2232 .put = ad1988_spdif_playback_source_put,
2233 },
2234 { } /* end */
2235};
2236
2237static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
2238 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2239 { } /* end */
2240};
2241
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002242static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
2243 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002244 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002245 { } /* end */
2246};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002247
2248/*
2249 * initialization verbs
2250 */
2251
2252/*
2253 * for 6-stack (+dig)
2254 */
2255static struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002256 /* Front, Surround, CLFE, side DAC; unmute as default */
2257 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2258 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2259 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2260 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002261 /* Port-A front headphon path */
2262 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2263 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2264 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2265 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2266 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2267 /* Port-D line-out path */
2268 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2269 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2270 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2271 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2272 /* Port-F surround path */
2273 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2274 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2275 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2276 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2277 /* Port-G CLFE path */
2278 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2279 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2280 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2281 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2282 /* Port-H side path */
2283 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2284 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2285 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2286 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2287 /* Mono out path */
2288 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2289 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2290 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2291 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2292 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2293 /* Port-B front mic-in path */
2294 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2295 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2296 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2297 /* Port-C line-in path */
2298 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2299 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2300 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2301 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2302 /* Port-E mic-in path */
2303 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2304 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2305 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2306 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002307 /* Analog CD Input */
2308 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002309 /* Analog Mix output amp */
2310 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002311
2312 { }
2313};
2314
2315static struct hda_verb ad1988_capture_init_verbs[] = {
2316 /* mute analog mix */
2317 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2318 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2319 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2320 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2321 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2322 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2323 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2324 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2325 /* select ADCs - front-mic */
2326 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2327 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2328 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002329
2330 { }
2331};
2332
2333static struct hda_verb ad1988_spdif_init_verbs[] = {
2334 /* SPDIF out sel */
2335 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2336 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2337 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002338 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002339 /* SPDIF out pin */
2340 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002341
2342 { }
2343};
2344
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002345/* AD1989 has no ADC -> SPDIF route */
2346static struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002347 /* SPDIF-1 out pin */
2348 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002349 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002350 /* SPDIF-2/HDMI out pin */
2351 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2352 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002353 { }
2354};
2355
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002356/*
2357 * verbs for 3stack (+dig)
2358 */
2359static struct hda_verb ad1988_3stack_ch2_init[] = {
2360 /* set port-C to line-in */
2361 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2362 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2363 /* set port-E to mic-in */
2364 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2365 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2366 { } /* end */
2367};
2368
2369static struct hda_verb ad1988_3stack_ch6_init[] = {
2370 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002371 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002372 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002373 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002374 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002375 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002376 { } /* end */
2377};
2378
2379static struct hda_channel_mode ad1988_3stack_modes[2] = {
2380 { 2, ad1988_3stack_ch2_init },
2381 { 6, ad1988_3stack_ch6_init },
2382};
2383
2384static struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002385 /* Front, Surround, CLFE, side DAC; unmute as default */
2386 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2387 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2388 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2389 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002390 /* Port-A front headphon path */
2391 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2392 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2393 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2394 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2395 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2396 /* Port-D line-out path */
2397 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2398 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2399 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2400 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2401 /* Mono out path */
2402 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2403 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2404 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2405 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2406 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2407 /* Port-B front mic-in path */
2408 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2409 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2410 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002411 /* Port-C line-in/surround path - 6ch mode as default */
2412 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2413 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002414 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002415 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002416 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002417 /* Port-E mic-in/CLFE path - 6ch mode as default */
2418 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2419 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002420 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002421 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002422 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2423 /* mute analog mix */
2424 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2425 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2426 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2427 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2428 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2429 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2430 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2431 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2432 /* select ADCs - front-mic */
2433 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2434 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2435 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002436 /* Analog Mix output amp */
2437 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002438 { }
2439};
2440
2441/*
2442 * verbs for laptop mode (+dig)
2443 */
2444static struct hda_verb ad1988_laptop_hp_on[] = {
2445 /* unmute port-A and mute port-D */
2446 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2447 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2448 { } /* end */
2449};
2450static struct hda_verb ad1988_laptop_hp_off[] = {
2451 /* mute port-A and unmute port-D */
2452 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2453 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2454 { } /* end */
2455};
2456
2457#define AD1988_HP_EVENT 0x01
2458
2459static struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002460 /* Front, Surround, CLFE, side DAC; unmute as default */
2461 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2462 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2463 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2464 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002465 /* Port-A front headphon path */
2466 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2467 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2468 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2469 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2470 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2471 /* unsolicited event for pin-sense */
2472 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2473 /* Port-D line-out path + EAPD */
2474 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2475 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2476 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2477 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2478 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2479 /* Mono out path */
2480 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2481 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2482 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2483 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2484 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2485 /* Port-B mic-in path */
2486 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2487 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2488 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2489 /* Port-C docking station - try to output */
2490 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2491 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2492 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2493 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2494 /* mute analog mix */
2495 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2496 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2497 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2498 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2499 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2500 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2501 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2502 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2503 /* select ADCs - mic */
2504 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2505 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2506 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002507 /* Analog Mix output amp */
2508 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002509 { }
2510};
2511
2512static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2513{
2514 if ((res >> 26) != AD1988_HP_EVENT)
2515 return;
2516 if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31))
2517 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2518 else
2519 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2520}
2521
Takashi Iwaicb53c622007-08-10 17:21:45 +02002522#ifdef CONFIG_SND_HDA_POWER_SAVE
2523static struct hda_amp_list ad1988_loopbacks[] = {
2524 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2525 { 0x20, HDA_INPUT, 1 }, /* Line */
2526 { 0x20, HDA_INPUT, 4 }, /* Mic */
2527 { 0x20, HDA_INPUT, 6 }, /* CD */
2528 { } /* end */
2529};
2530#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002531
2532/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002533 * Automatic parse of I/O pins from the BIOS configuration
2534 */
2535
Takashi Iwaid32410b12005-11-24 16:06:23 +01002536enum {
2537 AD_CTL_WIDGET_VOL,
2538 AD_CTL_WIDGET_MUTE,
2539 AD_CTL_BIND_MUTE,
2540};
2541static struct snd_kcontrol_new ad1988_control_templates[] = {
2542 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2543 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2544 HDA_BIND_MUTE(NULL, 0, 0, 0),
2545};
2546
2547/* add dynamic controls */
2548static int add_control(struct ad198x_spec *spec, int type, const char *name,
2549 unsigned long val)
2550{
2551 struct snd_kcontrol_new *knew;
2552
Takashi Iwai603c4012008-07-30 15:01:44 +02002553 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2554 knew = snd_array_new(&spec->kctls);
2555 if (!knew)
2556 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002557 *knew = ad1988_control_templates[type];
2558 knew->name = kstrdup(name, GFP_KERNEL);
2559 if (! knew->name)
2560 return -ENOMEM;
2561 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002562 return 0;
2563}
2564
2565#define AD1988_PIN_CD_NID 0x18
2566#define AD1988_PIN_BEEP_NID 0x10
2567
2568static hda_nid_t ad1988_mixer_nids[8] = {
2569 /* A B C D E F G H */
2570 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2571};
2572
2573static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2574{
2575 static hda_nid_t idx_to_dac[8] = {
2576 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002577 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002578 };
2579 static hda_nid_t idx_to_dac_rev2[8] = {
2580 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002581 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002582 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002583 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002584 return idx_to_dac_rev2[idx];
2585 else
2586 return idx_to_dac[idx];
2587}
2588
2589static hda_nid_t ad1988_boost_nids[8] = {
2590 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2591};
2592
2593static int ad1988_pin_idx(hda_nid_t nid)
2594{
2595 static hda_nid_t ad1988_io_pins[8] = {
2596 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2597 };
2598 int i;
2599 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2600 if (ad1988_io_pins[i] == nid)
2601 return i;
2602 return 0; /* should be -1 */
2603}
2604
2605static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2606{
2607 static int loopback_idx[8] = {
2608 2, 0, 1, 3, 4, 5, 1, 4
2609 };
2610 switch (nid) {
2611 case AD1988_PIN_CD_NID:
2612 return 6;
2613 default:
2614 return loopback_idx[ad1988_pin_idx(nid)];
2615 }
2616}
2617
2618static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2619{
2620 static int adc_idx[8] = {
2621 0, 1, 2, 8, 4, 3, 6, 7
2622 };
2623 switch (nid) {
2624 case AD1988_PIN_CD_NID:
2625 return 5;
2626 default:
2627 return adc_idx[ad1988_pin_idx(nid)];
2628 }
2629}
2630
2631/* fill in the dac_nids table from the parsed pin configuration */
2632static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2633 const struct auto_pin_cfg *cfg)
2634{
2635 struct ad198x_spec *spec = codec->spec;
2636 int i, idx;
2637
2638 spec->multiout.dac_nids = spec->private_dac_nids;
2639
2640 /* check the pins hardwired to audio widget */
2641 for (i = 0; i < cfg->line_outs; i++) {
2642 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2643 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2644 }
2645 spec->multiout.num_dacs = cfg->line_outs;
2646 return 0;
2647}
2648
2649/* add playback controls from the parsed DAC table */
2650static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2651 const struct auto_pin_cfg *cfg)
2652{
2653 char name[32];
2654 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2655 hda_nid_t nid;
2656 int i, err;
2657
2658 for (i = 0; i < cfg->line_outs; i++) {
2659 hda_nid_t dac = spec->multiout.dac_nids[i];
2660 if (! dac)
2661 continue;
2662 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2663 if (i == 2) {
2664 /* Center/LFE */
2665 err = add_control(spec, AD_CTL_WIDGET_VOL,
2666 "Center Playback Volume",
2667 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2668 if (err < 0)
2669 return err;
2670 err = add_control(spec, AD_CTL_WIDGET_VOL,
2671 "LFE Playback Volume",
2672 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2673 if (err < 0)
2674 return err;
2675 err = add_control(spec, AD_CTL_BIND_MUTE,
2676 "Center Playback Switch",
2677 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2678 if (err < 0)
2679 return err;
2680 err = add_control(spec, AD_CTL_BIND_MUTE,
2681 "LFE Playback Switch",
2682 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2683 if (err < 0)
2684 return err;
2685 } else {
2686 sprintf(name, "%s Playback Volume", chname[i]);
2687 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2688 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2689 if (err < 0)
2690 return err;
2691 sprintf(name, "%s Playback Switch", chname[i]);
2692 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2693 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2694 if (err < 0)
2695 return err;
2696 }
2697 }
2698 return 0;
2699}
2700
2701/* add playback controls for speaker and HP outputs */
2702static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2703 const char *pfx)
2704{
2705 struct ad198x_spec *spec = codec->spec;
2706 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02002707 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002708 char name[32];
2709
2710 if (! pin)
2711 return 0;
2712
2713 idx = ad1988_pin_idx(pin);
2714 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02002715 /* check whether the corresponding DAC was already taken */
2716 for (i = 0; i < spec->autocfg.line_outs; i++) {
2717 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2718 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
2719 if (dac == nid)
2720 break;
2721 }
2722 if (i >= spec->autocfg.line_outs) {
2723 /* specify the DAC as the extra output */
2724 if (!spec->multiout.hp_nid)
2725 spec->multiout.hp_nid = nid;
2726 else
2727 spec->multiout.extra_out_nid[0] = nid;
2728 /* control HP volume/switch on the output mixer amp */
2729 sprintf(name, "%s Playback Volume", pfx);
2730 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2731 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2732 if (err < 0)
2733 return err;
2734 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01002735 nid = ad1988_mixer_nids[idx];
2736 sprintf(name, "%s Playback Switch", pfx);
2737 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2738 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2739 return err;
2740 return 0;
2741}
2742
2743/* create input playback/capture controls for the given pin */
2744static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2745 const char *ctlname, int boost)
2746{
2747 char name[32];
2748 int err, idx;
2749
2750 sprintf(name, "%s Playback Volume", ctlname);
2751 idx = ad1988_pin_to_loopback_idx(pin);
2752 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2753 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2754 return err;
2755 sprintf(name, "%s Playback Switch", ctlname);
2756 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2757 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2758 return err;
2759 if (boost) {
2760 hda_nid_t bnid;
2761 idx = ad1988_pin_idx(pin);
2762 bnid = ad1988_boost_nids[idx];
2763 if (bnid) {
2764 sprintf(name, "%s Boost", ctlname);
2765 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2766 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2767
2768 }
2769 }
2770 return 0;
2771}
2772
2773/* create playback/capture controls for input pins */
2774static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2775 const struct auto_pin_cfg *cfg)
2776{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002777 struct hda_input_mux *imux = &spec->private_imux;
2778 int i, err;
2779
2780 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002781 err = new_analog_input(spec, cfg->input_pins[i],
2782 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002783 i <= AUTO_PIN_FRONT_MIC);
2784 if (err < 0)
2785 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002786 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002787 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2788 imux->num_items++;
2789 }
2790 imux->items[imux->num_items].label = "Mix";
2791 imux->items[imux->num_items].index = 9;
2792 imux->num_items++;
2793
2794 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2795 "Analog Mix Playback Volume",
2796 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2797 return err;
2798 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2799 "Analog Mix Playback Switch",
2800 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2801 return err;
2802
2803 return 0;
2804}
2805
2806static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2807 hda_nid_t nid, int pin_type,
2808 int dac_idx)
2809{
2810 /* set as output */
2811 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2812 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2813 switch (nid) {
2814 case 0x11: /* port-A - DAC 04 */
2815 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2816 break;
2817 case 0x14: /* port-B - DAC 06 */
2818 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2819 break;
2820 case 0x15: /* port-C - DAC 05 */
2821 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2822 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002823 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002824 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2825 break;
2826 case 0x13: /* mono - DAC 04 */
2827 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2828 break;
2829 }
2830}
2831
2832static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2833{
2834 struct ad198x_spec *spec = codec->spec;
2835 int i;
2836
2837 for (i = 0; i < spec->autocfg.line_outs; i++) {
2838 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2839 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2840 }
2841}
2842
2843static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2844{
2845 struct ad198x_spec *spec = codec->spec;
2846 hda_nid_t pin;
2847
Takashi Iwai82bc9552006-03-21 11:24:42 +01002848 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002849 if (pin) /* connect to front */
2850 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002851 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002852 if (pin) /* connect to front */
2853 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2854}
2855
2856static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2857{
2858 struct ad198x_spec *spec = codec->spec;
2859 int i, idx;
2860
2861 for (i = 0; i < AUTO_PIN_LAST; i++) {
2862 hda_nid_t nid = spec->autocfg.input_pins[i];
2863 if (! nid)
2864 continue;
2865 switch (nid) {
2866 case 0x15: /* port-C */
2867 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2868 break;
2869 case 0x17: /* port-E */
2870 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2871 break;
2872 }
2873 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2874 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2875 if (nid != AD1988_PIN_CD_NID)
2876 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
2877 AMP_OUT_MUTE);
2878 idx = ad1988_pin_idx(nid);
2879 if (ad1988_boost_nids[idx])
2880 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
2881 AC_VERB_SET_AMP_GAIN_MUTE,
2882 AMP_OUT_ZERO);
2883 }
2884}
2885
2886/* parse the BIOS configuration and set up the alc_spec */
2887/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
2888static int ad1988_parse_auto_config(struct hda_codec *codec)
2889{
2890 struct ad198x_spec *spec = codec->spec;
2891 int err;
2892
Kailang Yangdf694da2005-12-05 19:42:22 +01002893 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002894 return err;
2895 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
2896 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002897 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002898 return 0; /* can't find valid BIOS pin config */
2899 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01002900 (err = ad1988_auto_create_extra_out(codec,
2901 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002902 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002903 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002904 "Headphone")) < 0 ||
2905 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
2906 return err;
2907
2908 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2909
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002910 if (spec->autocfg.dig_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002911 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2912 if (spec->autocfg.dig_in_pin)
2913 spec->dig_in_nid = AD1988_SPDIF_IN;
2914
Takashi Iwai603c4012008-07-30 15:01:44 +02002915 if (spec->kctls.list)
2916 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002917
2918 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
2919
2920 spec->input_mux = &spec->private_imux;
2921
2922 return 1;
2923}
2924
2925/* init callback for auto-configuration model -- overriding the default init */
2926static int ad1988_auto_init(struct hda_codec *codec)
2927{
2928 ad198x_init(codec);
2929 ad1988_auto_init_multi_out(codec);
2930 ad1988_auto_init_extra_out(codec);
2931 ad1988_auto_init_analog_input(codec);
2932 return 0;
2933}
2934
2935
2936/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002937 */
2938
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002939static const char *ad1988_models[AD1988_MODEL_LAST] = {
2940 [AD1988_6STACK] = "6stack",
2941 [AD1988_6STACK_DIG] = "6stack-dig",
2942 [AD1988_3STACK] = "3stack",
2943 [AD1988_3STACK_DIG] = "3stack-dig",
2944 [AD1988_LAPTOP] = "laptop",
2945 [AD1988_LAPTOP_DIG] = "laptop-dig",
2946 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002947};
2948
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002949static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002950 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002951 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02002952 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07002953 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002954 {}
2955};
2956
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002957static int patch_ad1988(struct hda_codec *codec)
2958{
2959 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002960 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002961
2962 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2963 if (spec == NULL)
2964 return -ENOMEM;
2965
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002966 codec->spec = spec;
2967
Takashi Iwai1a806f42006-07-03 15:58:16 +02002968 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002969 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
2970
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002971 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002972 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002973 if (board_config < 0) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002974 printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n");
2975 board_config = AD1988_AUTO;
2976 }
2977
2978 if (board_config == AD1988_AUTO) {
2979 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002980 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002981 if (err < 0) {
2982 ad198x_free(codec);
2983 return err;
2984 } else if (! err) {
2985 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
2986 board_config = AD1988_6STACK;
2987 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002988 }
2989
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002990 err = snd_hda_attach_beep_device(codec, 0x10);
2991 if (err < 0) {
2992 ad198x_free(codec);
2993 return err;
2994 }
2995 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
2996
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002997 switch (board_config) {
2998 case AD1988_6STACK:
2999 case AD1988_6STACK_DIG:
3000 spec->multiout.max_channels = 8;
3001 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003002 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003003 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3004 else
3005 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003006 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003007 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003008 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003009 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3010 else
3011 spec->mixers[0] = ad1988_6stack_mixers1;
3012 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003013 spec->num_init_verbs = 1;
3014 spec->init_verbs[0] = ad1988_6stack_init_verbs;
3015 if (board_config == AD1988_6STACK_DIG) {
3016 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3017 spec->dig_in_nid = AD1988_SPDIF_IN;
3018 }
3019 break;
3020 case AD1988_3STACK:
3021 case AD1988_3STACK_DIG:
3022 spec->multiout.max_channels = 6;
3023 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003024 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003025 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3026 else
3027 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003028 spec->input_mux = &ad1988_6stack_capture_source;
3029 spec->channel_mode = ad1988_3stack_modes;
3030 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003031 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003032 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003033 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3034 else
3035 spec->mixers[0] = ad1988_3stack_mixers1;
3036 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003037 spec->num_init_verbs = 1;
3038 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3039 if (board_config == AD1988_3STACK_DIG)
3040 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3041 break;
3042 case AD1988_LAPTOP:
3043 case AD1988_LAPTOP_DIG:
3044 spec->multiout.max_channels = 2;
3045 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003046 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003047 spec->input_mux = &ad1988_laptop_capture_source;
3048 spec->num_mixers = 1;
3049 spec->mixers[0] = ad1988_laptop_mixers;
3050 spec->num_init_verbs = 1;
3051 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3052 if (board_config == AD1988_LAPTOP_DIG)
3053 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3054 break;
3055 }
3056
Takashi Iwaid32410b12005-11-24 16:06:23 +01003057 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3058 spec->adc_nids = ad1988_adc_nids;
3059 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003060 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3061 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3062 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003063 if (codec->vendor_id >= 0x11d4989a) {
3064 spec->mixers[spec->num_mixers++] =
3065 ad1989_spdif_out_mixers;
3066 spec->init_verbs[spec->num_init_verbs++] =
3067 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003068 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003069 } else {
3070 spec->mixers[spec->num_mixers++] =
3071 ad1988_spdif_out_mixers;
3072 spec->init_verbs[spec->num_init_verbs++] =
3073 ad1988_spdif_init_verbs;
3074 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003075 }
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003076 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003077 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
3078
3079 codec->patch_ops = ad198x_patch_ops;
3080 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003081 case AD1988_AUTO:
3082 codec->patch_ops.init = ad1988_auto_init;
3083 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003084 case AD1988_LAPTOP:
3085 case AD1988_LAPTOP_DIG:
3086 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3087 break;
3088 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003089#ifdef CONFIG_SND_HDA_POWER_SAVE
3090 spec->loopback.amplist = ad1988_loopbacks;
3091#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003092 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003093
3094 return 0;
3095}
3096
3097
3098/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003099 * AD1884 / AD1984
3100 *
3101 * port-B - front line/mic-in
3102 * port-E - aux in/out
3103 * port-F - aux in/out
3104 * port-C - rear line/mic-in
3105 * port-D - rear line/hp-out
3106 * port-A - front line/hp-out
3107 *
3108 * AD1984 = AD1884 + two digital mic-ins
3109 *
3110 * FIXME:
3111 * For simplicity, we share the single DAC for both HP and line-outs
3112 * right now. The inidividual playbacks could be easily implemented,
3113 * but no build-up framework is given, so far.
3114 */
3115
3116static hda_nid_t ad1884_dac_nids[1] = {
3117 0x04,
3118};
3119
3120static hda_nid_t ad1884_adc_nids[2] = {
3121 0x08, 0x09,
3122};
3123
3124static hda_nid_t ad1884_capsrc_nids[2] = {
3125 0x0c, 0x0d,
3126};
3127
3128#define AD1884_SPDIF_OUT 0x02
3129
3130static struct hda_input_mux ad1884_capture_source = {
3131 .num_items = 4,
3132 .items = {
3133 { "Front Mic", 0x0 },
3134 { "Mic", 0x1 },
3135 { "CD", 0x2 },
3136 { "Mix", 0x3 },
3137 },
3138};
3139
3140static struct snd_kcontrol_new ad1884_base_mixers[] = {
3141 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3142 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3143 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3144 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3145 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3146 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3147 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3148 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3149 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3150 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3151 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3152 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003153 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
3154 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3155 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3156 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3157 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3158 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3159 {
3160 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3161 /* The multiple "Capture Source" controls confuse alsamixer
3162 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003163 */
3164 /* .name = "Capture Source", */
3165 .name = "Input Source",
3166 .count = 2,
3167 .info = ad198x_mux_enum_info,
3168 .get = ad198x_mux_enum_get,
3169 .put = ad198x_mux_enum_put,
3170 },
3171 /* SPDIF controls */
3172 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3173 {
3174 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3175 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3176 /* identical with ad1983 */
3177 .info = ad1983_spdif_route_info,
3178 .get = ad1983_spdif_route_get,
3179 .put = ad1983_spdif_route_put,
3180 },
3181 { } /* end */
3182};
3183
3184static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3185 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3186 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3187 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003188 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003189 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003190 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003191 { } /* end */
3192};
3193
3194/*
3195 * initialization verbs
3196 */
3197static struct hda_verb ad1884_init_verbs[] = {
3198 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003199 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3200 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003201 /* Port-A (HP) mixer */
3202 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3203 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3204 /* Port-A pin */
3205 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3206 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3207 /* HP selector - select DAC2 */
3208 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3209 /* Port-D (Line-out) mixer */
3210 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3211 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3212 /* Port-D pin */
3213 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3214 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3215 /* Mono-out mixer */
3216 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3217 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3218 /* Mono-out pin */
3219 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3220 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3221 /* Mono selector */
3222 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3223 /* Port-B (front mic) pin */
3224 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003225 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003226 /* Port-C (rear mic) pin */
3227 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003228 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003229 /* Analog mixer; mute as default */
3230 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3231 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3232 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3233 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3234 /* Analog Mix output amp */
3235 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3236 /* SPDIF output selector */
3237 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3238 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3239 { } /* end */
3240};
3241
Takashi Iwaicb53c622007-08-10 17:21:45 +02003242#ifdef CONFIG_SND_HDA_POWER_SAVE
3243static struct hda_amp_list ad1884_loopbacks[] = {
3244 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3245 { 0x20, HDA_INPUT, 1 }, /* Mic */
3246 { 0x20, HDA_INPUT, 2 }, /* CD */
3247 { 0x20, HDA_INPUT, 4 }, /* Docking */
3248 { } /* end */
3249};
3250#endif
3251
Takashi Iwai2134ea42008-01-10 16:53:55 +01003252static const char *ad1884_slave_vols[] = {
3253 "PCM Playback Volume",
3254 "Mic Playback Volume",
3255 "Mono Playback Volume",
3256 "Front Mic Playback Volume",
3257 "Mic Playback Volume",
3258 "CD Playback Volume",
3259 "Internal Mic Playback Volume",
3260 "Docking Mic Playback Volume"
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003261 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003262 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003263 NULL
3264};
3265
Takashi Iwai2bac6472007-05-18 18:21:41 +02003266static int patch_ad1884(struct hda_codec *codec)
3267{
3268 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003269 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003270
3271 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3272 if (spec == NULL)
3273 return -ENOMEM;
3274
Takashi Iwai2bac6472007-05-18 18:21:41 +02003275 codec->spec = spec;
3276
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003277 err = snd_hda_attach_beep_device(codec, 0x10);
3278 if (err < 0) {
3279 ad198x_free(codec);
3280 return err;
3281 }
3282 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3283
Takashi Iwai2bac6472007-05-18 18:21:41 +02003284 spec->multiout.max_channels = 2;
3285 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3286 spec->multiout.dac_nids = ad1884_dac_nids;
3287 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3288 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3289 spec->adc_nids = ad1884_adc_nids;
3290 spec->capsrc_nids = ad1884_capsrc_nids;
3291 spec->input_mux = &ad1884_capture_source;
3292 spec->num_mixers = 1;
3293 spec->mixers[0] = ad1884_base_mixers;
3294 spec->num_init_verbs = 1;
3295 spec->init_verbs[0] = ad1884_init_verbs;
3296 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003297#ifdef CONFIG_SND_HDA_POWER_SAVE
3298 spec->loopback.amplist = ad1884_loopbacks;
3299#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003300 spec->vmaster_nid = 0x04;
3301 /* we need to cover all playback volumes */
3302 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003303
3304 codec->patch_ops = ad198x_patch_ops;
3305
3306 return 0;
3307}
3308
3309/*
3310 * Lenovo Thinkpad T61/X61
3311 */
3312static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003313 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003314 .items = {
3315 { "Mic", 0x0 },
3316 { "Internal Mic", 0x1 },
3317 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003318 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003319 },
3320};
3321
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003322
3323/*
3324 * Dell Precision T3400
3325 */
3326static struct hda_input_mux ad1984_dell_desktop_capture_source = {
3327 .num_items = 3,
3328 .items = {
3329 { "Front Mic", 0x0 },
3330 { "Line-In", 0x1 },
3331 { "Mix", 0x3 },
3332 },
3333};
3334
3335
Takashi Iwai2bac6472007-05-18 18:21:41 +02003336static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3337 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3338 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3339 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3340 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3341 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3342 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3343 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3344 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3345 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3346 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003347 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3348 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003349 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003350 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3351 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3352 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3353 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3354 {
3355 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3356 /* The multiple "Capture Source" controls confuse alsamixer
3357 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003358 */
3359 /* .name = "Capture Source", */
3360 .name = "Input Source",
3361 .count = 2,
3362 .info = ad198x_mux_enum_info,
3363 .get = ad198x_mux_enum_get,
3364 .put = ad198x_mux_enum_put,
3365 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003366 /* SPDIF controls */
3367 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3368 {
3369 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3370 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3371 /* identical with ad1983 */
3372 .info = ad1983_spdif_route_info,
3373 .get = ad1983_spdif_route_get,
3374 .put = ad1983_spdif_route_put,
3375 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003376 { } /* end */
3377};
3378
3379/* additional verbs */
3380static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3381 /* Port-E (docking station mic) pin */
3382 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3383 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3384 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003385 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003386 /* Analog mixer - docking mic; mute as default */
3387 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003388 /* enable EAPD bit */
3389 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003390 { } /* end */
3391};
3392
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003393/*
3394 * Dell Precision T3400
3395 */
3396static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
3397 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3398 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3399 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3400 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3401 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3402 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3403 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3404 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3405 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003406 HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
3407 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3408 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3409 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3410 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3411 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3412 {
3413 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3414 /* The multiple "Capture Source" controls confuse alsamixer
3415 * So call somewhat different..
3416 */
3417 /* .name = "Capture Source", */
3418 .name = "Input Source",
3419 .count = 2,
3420 .info = ad198x_mux_enum_info,
3421 .get = ad198x_mux_enum_get,
3422 .put = ad198x_mux_enum_put,
3423 },
3424 { } /* end */
3425};
3426
Takashi Iwai2bac6472007-05-18 18:21:41 +02003427/* Digial MIC ADC NID 0x05 + 0x06 */
3428static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3429 struct hda_codec *codec,
3430 unsigned int stream_tag,
3431 unsigned int format,
3432 struct snd_pcm_substream *substream)
3433{
3434 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3435 stream_tag, 0, format);
3436 return 0;
3437}
3438
3439static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3440 struct hda_codec *codec,
3441 struct snd_pcm_substream *substream)
3442{
Takashi Iwai888afa12008-03-18 09:57:50 +01003443 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003444 return 0;
3445}
3446
3447static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3448 .substreams = 2,
3449 .channels_min = 2,
3450 .channels_max = 2,
3451 .nid = 0x05,
3452 .ops = {
3453 .prepare = ad1984_pcm_dmic_prepare,
3454 .cleanup = ad1984_pcm_dmic_cleanup
3455 },
3456};
3457
3458static int ad1984_build_pcms(struct hda_codec *codec)
3459{
3460 struct ad198x_spec *spec = codec->spec;
3461 struct hda_pcm *info;
3462 int err;
3463
3464 err = ad198x_build_pcms(codec);
3465 if (err < 0)
3466 return err;
3467
3468 info = spec->pcm_rec + codec->num_pcms;
3469 codec->num_pcms++;
3470 info->name = "AD1984 Digital Mic";
3471 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3472 return 0;
3473}
3474
3475/* models */
3476enum {
3477 AD1984_BASIC,
3478 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003479 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003480 AD1984_MODELS
3481};
3482
3483static const char *ad1984_models[AD1984_MODELS] = {
3484 [AD1984_BASIC] = "basic",
3485 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003486 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003487};
3488
3489static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3490 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003491 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003492 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003493 {}
3494};
3495
3496static int patch_ad1984(struct hda_codec *codec)
3497{
3498 struct ad198x_spec *spec;
3499 int board_config, err;
3500
3501 err = patch_ad1884(codec);
3502 if (err < 0)
3503 return err;
3504 spec = codec->spec;
3505 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3506 ad1984_models, ad1984_cfg_tbl);
3507 switch (board_config) {
3508 case AD1984_BASIC:
3509 /* additional digital mics */
3510 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3511 codec->patch_ops.build_pcms = ad1984_build_pcms;
3512 break;
3513 case AD1984_THINKPAD:
Jerone Youngebf00c52008-01-07 12:22:18 +01003514 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003515 spec->input_mux = &ad1984_thinkpad_capture_source;
3516 spec->mixers[0] = ad1984_thinkpad_mixers;
3517 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
3518 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003519 case AD1984_DELL_DESKTOP:
3520 spec->multiout.dig_out_nid = 0;
3521 spec->input_mux = &ad1984_dell_desktop_capture_source;
3522 spec->mixers[0] = ad1984_dell_desktop_mixers;
3523 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003524 }
3525 return 0;
3526}
3527
3528
3529/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003530 * AD1883 / AD1884A / AD1984A / AD1984B
3531 *
3532 * port-B (0x14) - front mic-in
3533 * port-E (0x1c) - rear mic-in
3534 * port-F (0x16) - CD / ext out
3535 * port-C (0x15) - rear line-in
3536 * port-D (0x12) - rear line-out
3537 * port-A (0x11) - front hp-out
3538 *
3539 * AD1984A = AD1884A + digital-mic
3540 * AD1883 = equivalent with AD1984A
3541 * AD1984B = AD1984A + extra SPDIF-out
3542 *
3543 * FIXME:
3544 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3545 */
3546
3547static hda_nid_t ad1884a_dac_nids[1] = {
3548 0x03,
3549};
3550
3551#define ad1884a_adc_nids ad1884_adc_nids
3552#define ad1884a_capsrc_nids ad1884_capsrc_nids
3553
3554#define AD1884A_SPDIF_OUT 0x02
3555
3556static struct hda_input_mux ad1884a_capture_source = {
3557 .num_items = 5,
3558 .items = {
3559 { "Front Mic", 0x0 },
3560 { "Mic", 0x4 },
3561 { "Line", 0x1 },
3562 { "CD", 0x2 },
3563 { "Mix", 0x3 },
3564 },
3565};
3566
3567static struct snd_kcontrol_new ad1884a_base_mixers[] = {
3568 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3569 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3570 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3571 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3572 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3573 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3574 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3575 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3576 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3577 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3578 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3579 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3580 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3581 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3582 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3583 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003584 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3585 HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
3586 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3587 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3588 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3589 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3590 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3591 {
3592 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3593 /* The multiple "Capture Source" controls confuse alsamixer
3594 * So call somewhat different..
3595 */
3596 /* .name = "Capture Source", */
3597 .name = "Input Source",
3598 .count = 2,
3599 .info = ad198x_mux_enum_info,
3600 .get = ad198x_mux_enum_get,
3601 .put = ad198x_mux_enum_put,
3602 },
3603 /* SPDIF controls */
3604 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3605 {
3606 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3607 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3608 /* identical with ad1983 */
3609 .info = ad1983_spdif_route_info,
3610 .get = ad1983_spdif_route_get,
3611 .put = ad1983_spdif_route_put,
3612 },
3613 { } /* end */
3614};
3615
3616/*
3617 * initialization verbs
3618 */
3619static struct hda_verb ad1884a_init_verbs[] = {
3620 /* DACs; unmute as default */
3621 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3622 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3623 /* Port-A (HP) mixer - route only from analog mixer */
3624 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3625 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3626 /* Port-A pin */
3627 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3628 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3629 /* Port-D (Line-out) mixer - route only from analog mixer */
3630 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3631 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3632 /* Port-D pin */
3633 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3634 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3635 /* Mono-out mixer - route only from analog mixer */
3636 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3637 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3638 /* Mono-out pin */
3639 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3640 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3641 /* Port-B (front mic) pin */
3642 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003643 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003644 /* Port-C (rear line-in) pin */
3645 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003646 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003647 /* Port-E (rear mic) pin */
3648 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3649 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3650 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
3651 /* Port-F (CD) pin */
3652 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3653 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3654 /* Analog mixer; mute as default */
3655 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3656 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3657 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3658 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3659 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
3660 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3661 /* Analog Mix output amp */
3662 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3663 /* capture sources */
3664 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
3665 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3666 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3667 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3668 /* SPDIF output amp */
3669 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3670 { } /* end */
3671};
3672
3673#ifdef CONFIG_SND_HDA_POWER_SAVE
3674static struct hda_amp_list ad1884a_loopbacks[] = {
3675 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3676 { 0x20, HDA_INPUT, 1 }, /* Mic */
3677 { 0x20, HDA_INPUT, 2 }, /* CD */
3678 { 0x20, HDA_INPUT, 4 }, /* Docking */
3679 { } /* end */
3680};
3681#endif
3682
3683/*
3684 * Laptop model
3685 *
3686 * Port A: Headphone jack
3687 * Port B: MIC jack
3688 * Port C: Internal MIC
3689 * Port D: Dock Line Out (if enabled)
3690 * Port E: Dock Line In (if enabled)
3691 * Port F: Internal speakers
3692 */
3693
3694static struct hda_input_mux ad1884a_laptop_capture_source = {
3695 .num_items = 4,
3696 .items = {
3697 { "Mic", 0x0 }, /* port-B */
3698 { "Internal Mic", 0x1 }, /* port-C */
3699 { "Dock Mic", 0x4 }, /* port-E */
3700 { "Mix", 0x3 },
3701 },
3702};
3703
3704static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
3705 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3706 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3707 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3708 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3709 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3710 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3711 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3712 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3713 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3714 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3715 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003716 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3717 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
3718 HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3719 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3720 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3721 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3722 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3723 {
3724 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3725 /* The multiple "Capture Source" controls confuse alsamixer
3726 * So call somewhat different..
3727 */
3728 /* .name = "Capture Source", */
3729 .name = "Input Source",
3730 .count = 2,
3731 .info = ad198x_mux_enum_info,
3732 .get = ad198x_mux_enum_get,
3733 .put = ad198x_mux_enum_put,
3734 },
3735 { } /* end */
3736};
3737
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003738static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
3739 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3740 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3741 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3742 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02003743 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
3744 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003745 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3746 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003747 { } /* end */
3748};
3749
Takashi Iwaic5059252008-02-16 09:43:56 +01003750/* mute internal speaker if HP is plugged */
3751static void ad1884a_hp_automute(struct hda_codec *codec)
3752{
3753 unsigned int present;
3754
3755 present = snd_hda_codec_read(codec, 0x11, 0,
3756 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
3757 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3758 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3759 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3760 present ? 0x00 : 0x02);
3761}
3762
Takashi Iwai269ef192008-05-30 15:32:15 +02003763/* switch to external mic if plugged */
3764static void ad1884a_hp_automic(struct hda_codec *codec)
3765{
3766 unsigned int present;
3767
3768 present = snd_hda_codec_read(codec, 0x14, 0,
3769 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
3770 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
3771 present ? 0 : 1);
3772}
3773
Takashi Iwaic5059252008-02-16 09:43:56 +01003774#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02003775#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01003776
3777/* unsolicited event for HP jack sensing */
3778static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
3779{
Takashi Iwai269ef192008-05-30 15:32:15 +02003780 switch (res >> 26) {
3781 case AD1884A_HP_EVENT:
3782 ad1884a_hp_automute(codec);
3783 break;
3784 case AD1884A_MIC_EVENT:
3785 ad1884a_hp_automic(codec);
3786 break;
3787 }
Takashi Iwaic5059252008-02-16 09:43:56 +01003788}
3789
3790/* initialize jack-sensing, too */
3791static int ad1884a_hp_init(struct hda_codec *codec)
3792{
3793 ad198x_init(codec);
3794 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02003795 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01003796 return 0;
3797}
3798
3799/* additional verbs for laptop model */
3800static struct hda_verb ad1884a_laptop_verbs[] = {
3801 /* Port-A (HP) pin - always unmuted */
3802 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3803 /* Port-F (int speaker) mixer - route only from analog mixer */
3804 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3805 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3806 /* Port-F pin */
3807 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3808 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Takashi Iwai269ef192008-05-30 15:32:15 +02003809 /* Port-C pin - internal mic-in */
3810 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3811 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3812 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwaic5059252008-02-16 09:43:56 +01003813 /* analog mix */
3814 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3815 /* unsolicited event for pin-sense */
3816 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003817 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaic5059252008-02-16 09:43:56 +01003818 { } /* end */
3819};
3820
3821/*
Takashi Iwaif0813742008-03-18 12:13:03 +01003822 * Thinkpad X300
3823 * 0x11 - HP
3824 * 0x12 - speaker
3825 * 0x14 - mic-in
3826 * 0x17 - built-in mic
3827 */
3828
3829static struct hda_verb ad1984a_thinkpad_verbs[] = {
3830 /* HP unmute */
3831 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3832 /* analog mix */
3833 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3834 /* turn on EAPD */
3835 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
3836 /* unsolicited event for pin-sense */
3837 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
3838 /* internal mic - dmic */
3839 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02003840 /* set magic COEFs for dmic */
3841 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
3842 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01003843 { } /* end */
3844};
3845
3846static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
3847 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3848 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3849 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3850 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3851 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3852 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01003853 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3854 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
3855 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3856 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3857 {
3858 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3859 .name = "Capture Source",
3860 .info = ad198x_mux_enum_info,
3861 .get = ad198x_mux_enum_get,
3862 .put = ad198x_mux_enum_put,
3863 },
3864 { } /* end */
3865};
3866
3867static struct hda_input_mux ad1984a_thinkpad_capture_source = {
3868 .num_items = 3,
3869 .items = {
3870 { "Mic", 0x0 },
3871 { "Internal Mic", 0x5 },
3872 { "Mix", 0x3 },
3873 },
3874};
3875
3876/* mute internal speaker if HP is plugged */
3877static void ad1984a_thinkpad_automute(struct hda_codec *codec)
3878{
3879 unsigned int present;
3880
3881 present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0)
3882 & AC_PINSENSE_PRESENCE;
3883 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
3884 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3885}
3886
3887/* unsolicited event for HP jack sensing */
3888static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
3889 unsigned int res)
3890{
3891 if ((res >> 26) != AD1884A_HP_EVENT)
3892 return;
3893 ad1984a_thinkpad_automute(codec);
3894}
3895
3896/* initialize jack-sensing, too */
3897static int ad1984a_thinkpad_init(struct hda_codec *codec)
3898{
3899 ad198x_init(codec);
3900 ad1984a_thinkpad_automute(codec);
3901 return 0;
3902}
3903
3904/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003905 */
3906
3907enum {
3908 AD1884A_DESKTOP,
3909 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003910 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01003911 AD1884A_THINKPAD,
Takashi Iwaic5059252008-02-16 09:43:56 +01003912 AD1884A_MODELS
3913};
3914
3915static const char *ad1884a_models[AD1884A_MODELS] = {
3916 [AD1884A_DESKTOP] = "desktop",
3917 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003918 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01003919 [AD1884A_THINKPAD] = "thinkpad",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003920};
3921
3922static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
3923 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01003924 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01003925 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01003926 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwai11d518e2008-12-10 10:37:33 +01003927 SND_PCI_QUIRK(0x103c, 0x30e6, "HP 6730b", AD1884A_LAPTOP),
Travis Place25424832008-11-10 17:56:23 +01003928 SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP),
Michel Marti65b92e52008-11-08 11:33:32 +01003929 SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP),
Takashi Iwaif0813742008-03-18 12:13:03 +01003930 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003931 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01003932};
3933
3934static int patch_ad1884a(struct hda_codec *codec)
3935{
3936 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003937 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01003938
3939 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3940 if (spec == NULL)
3941 return -ENOMEM;
3942
Takashi Iwaic5059252008-02-16 09:43:56 +01003943 codec->spec = spec;
3944
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003945 err = snd_hda_attach_beep_device(codec, 0x10);
3946 if (err < 0) {
3947 ad198x_free(codec);
3948 return err;
3949 }
3950 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3951
Takashi Iwaic5059252008-02-16 09:43:56 +01003952 spec->multiout.max_channels = 2;
3953 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
3954 spec->multiout.dac_nids = ad1884a_dac_nids;
3955 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
3956 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
3957 spec->adc_nids = ad1884a_adc_nids;
3958 spec->capsrc_nids = ad1884a_capsrc_nids;
3959 spec->input_mux = &ad1884a_capture_source;
3960 spec->num_mixers = 1;
3961 spec->mixers[0] = ad1884a_base_mixers;
3962 spec->num_init_verbs = 1;
3963 spec->init_verbs[0] = ad1884a_init_verbs;
3964 spec->spdif_route = 0;
3965#ifdef CONFIG_SND_HDA_POWER_SAVE
3966 spec->loopback.amplist = ad1884a_loopbacks;
3967#endif
3968 codec->patch_ops = ad198x_patch_ops;
3969
3970 /* override some parameters */
3971 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003972 ad1884a_models,
3973 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01003974 switch (board_config) {
3975 case AD1884A_LAPTOP:
3976 spec->mixers[0] = ad1884a_laptop_mixers;
3977 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
3978 spec->multiout.dig_out_nid = 0;
3979 spec->input_mux = &ad1884a_laptop_capture_source;
3980 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
3981 codec->patch_ops.init = ad1884a_hp_init;
3982 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003983 case AD1884A_MOBILE:
3984 spec->mixers[0] = ad1884a_mobile_mixers;
3985 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
3986 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003987 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
3988 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01003989 /* set the upper-limit for mixer amp to 0dB for avoiding the
3990 * possible damage by overloading
3991 */
3992 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
3993 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3994 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3995 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3996 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003997 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01003998 case AD1884A_THINKPAD:
3999 spec->mixers[0] = ad1984a_thinkpad_mixers;
4000 spec->init_verbs[spec->num_init_verbs++] =
4001 ad1984a_thinkpad_verbs;
4002 spec->multiout.dig_out_nid = 0;
4003 spec->input_mux = &ad1984a_thinkpad_capture_source;
4004 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4005 codec->patch_ops.init = ad1984a_thinkpad_init;
4006 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004007 }
4008
4009 return 0;
4010}
4011
4012
4013/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004014 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004015 *
4016 * port-A - front hp-out
4017 * port-B - front mic-in
4018 * port-C - rear line-in, shared surr-out (3stack)
4019 * port-D - rear line-out
4020 * port-E - rear mic-in, shared clfe-out (3stack)
4021 * port-F - rear surr-out (6stack)
4022 * port-G - rear clfe-out (6stack)
4023 */
4024
4025static hda_nid_t ad1882_dac_nids[3] = {
4026 0x04, 0x03, 0x05
4027};
4028
4029static hda_nid_t ad1882_adc_nids[2] = {
4030 0x08, 0x09,
4031};
4032
4033static hda_nid_t ad1882_capsrc_nids[2] = {
4034 0x0c, 0x0d,
4035};
4036
4037#define AD1882_SPDIF_OUT 0x02
4038
4039/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
4040static struct hda_input_mux ad1882_capture_source = {
4041 .num_items = 5,
4042 .items = {
4043 { "Front Mic", 0x1 },
4044 { "Mic", 0x4 },
4045 { "Line", 0x2 },
4046 { "CD", 0x3 },
4047 { "Mix", 0x7 },
4048 },
4049};
4050
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004051/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
4052static struct hda_input_mux ad1882a_capture_source = {
4053 .num_items = 5,
4054 .items = {
4055 { "Front Mic", 0x1 },
4056 { "Mic", 0x4},
4057 { "Line", 0x2 },
4058 { "Digital Mic", 0x06 },
4059 { "Mix", 0x7 },
4060 },
4061};
4062
Takashi Iwai0ac85512007-06-20 15:46:13 +02004063static struct snd_kcontrol_new ad1882_base_mixers[] = {
4064 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4065 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4066 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4067 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4068 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4069 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4070 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4071 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004072
Takashi Iwai0ac85512007-06-20 15:46:13 +02004073 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
4074 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
4075 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
4076 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4077 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4078 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4079 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4080 {
4081 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4082 /* The multiple "Capture Source" controls confuse alsamixer
4083 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004084 */
4085 /* .name = "Capture Source", */
4086 .name = "Input Source",
4087 .count = 2,
4088 .info = ad198x_mux_enum_info,
4089 .get = ad198x_mux_enum_get,
4090 .put = ad198x_mux_enum_put,
4091 },
4092 /* SPDIF controls */
4093 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4094 {
4095 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4096 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4097 /* identical with ad1983 */
4098 .info = ad1983_spdif_route_info,
4099 .get = ad1983_spdif_route_get,
4100 .put = ad1983_spdif_route_put,
4101 },
4102 { } /* end */
4103};
4104
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004105static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
4106 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4107 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4108 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4109 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4110 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4111 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4112 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4113 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004114 { } /* end */
4115};
4116
4117static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
4118 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4119 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4120 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4121 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4122 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4123 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4124 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4125 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004126 HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
4127 { } /* end */
4128};
4129
Takashi Iwai0ac85512007-06-20 15:46:13 +02004130static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
4131 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4132 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4133 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4134 {
4135 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4136 .name = "Channel Mode",
4137 .info = ad198x_ch_mode_info,
4138 .get = ad198x_ch_mode_get,
4139 .put = ad198x_ch_mode_put,
4140 },
4141 { } /* end */
4142};
4143
4144static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
4145 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4146 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4147 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4148 { } /* end */
4149};
4150
4151static struct hda_verb ad1882_ch2_init[] = {
4152 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4153 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4154 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4155 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4156 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4157 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4158 { } /* end */
4159};
4160
4161static struct hda_verb ad1882_ch4_init[] = {
4162 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4163 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4164 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4165 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4166 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4167 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4168 { } /* end */
4169};
4170
4171static struct hda_verb ad1882_ch6_init[] = {
4172 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4173 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4174 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4175 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4176 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4177 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4178 { } /* end */
4179};
4180
4181static struct hda_channel_mode ad1882_modes[3] = {
4182 { 2, ad1882_ch2_init },
4183 { 4, ad1882_ch4_init },
4184 { 6, ad1882_ch6_init },
4185};
4186
4187/*
4188 * initialization verbs
4189 */
4190static struct hda_verb ad1882_init_verbs[] = {
4191 /* DACs; mute as default */
4192 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4193 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4194 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4195 /* Port-A (HP) mixer */
4196 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4197 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4198 /* Port-A pin */
4199 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4200 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4201 /* HP selector - select DAC2 */
4202 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4203 /* Port-D (Line-out) mixer */
4204 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4205 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4206 /* Port-D pin */
4207 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4208 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4209 /* Mono-out mixer */
4210 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4211 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4212 /* Mono-out pin */
4213 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4214 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4215 /* Port-B (front mic) pin */
4216 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4217 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4218 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4219 /* Port-C (line-in) pin */
4220 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4221 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4222 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4223 /* Port-C mixer - mute as input */
4224 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4225 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4226 /* Port-E (mic-in) pin */
4227 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4228 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4229 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4230 /* Port-E mixer - mute as input */
4231 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4232 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4233 /* Port-F (surround) */
4234 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4235 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4236 /* Port-G (CLFE) */
4237 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4238 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4239 /* Analog mixer; mute as default */
4240 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4241 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4242 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4243 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4244 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4245 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4246 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4247 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4248 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4249 /* Analog Mix output amp */
4250 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4251 /* SPDIF output selector */
4252 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4253 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4254 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4255 { } /* end */
4256};
4257
Takashi Iwaicb53c622007-08-10 17:21:45 +02004258#ifdef CONFIG_SND_HDA_POWER_SAVE
4259static struct hda_amp_list ad1882_loopbacks[] = {
4260 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4261 { 0x20, HDA_INPUT, 1 }, /* Mic */
4262 { 0x20, HDA_INPUT, 4 }, /* Line */
4263 { 0x20, HDA_INPUT, 6 }, /* CD */
4264 { } /* end */
4265};
4266#endif
4267
Takashi Iwai0ac85512007-06-20 15:46:13 +02004268/* models */
4269enum {
4270 AD1882_3STACK,
4271 AD1882_6STACK,
4272 AD1882_MODELS
4273};
4274
4275static const char *ad1882_models[AD1986A_MODELS] = {
4276 [AD1882_3STACK] = "3stack",
4277 [AD1882_6STACK] = "6stack",
4278};
4279
4280
4281static int patch_ad1882(struct hda_codec *codec)
4282{
4283 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004284 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004285
4286 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4287 if (spec == NULL)
4288 return -ENOMEM;
4289
Takashi Iwai0ac85512007-06-20 15:46:13 +02004290 codec->spec = spec;
4291
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004292 err = snd_hda_attach_beep_device(codec, 0x10);
4293 if (err < 0) {
4294 ad198x_free(codec);
4295 return err;
4296 }
4297 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4298
Takashi Iwai0ac85512007-06-20 15:46:13 +02004299 spec->multiout.max_channels = 6;
4300 spec->multiout.num_dacs = 3;
4301 spec->multiout.dac_nids = ad1882_dac_nids;
4302 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
4303 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
4304 spec->adc_nids = ad1882_adc_nids;
4305 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004306 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004307 spec->input_mux = &ad1882_capture_source;
4308 else
4309 spec->input_mux = &ad1882a_capture_source;
4310 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004311 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004312 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004313 spec->mixers[1] = ad1882_loopback_mixers;
4314 else
4315 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004316 spec->num_init_verbs = 1;
4317 spec->init_verbs[0] = ad1882_init_verbs;
4318 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02004319#ifdef CONFIG_SND_HDA_POWER_SAVE
4320 spec->loopback.amplist = ad1882_loopbacks;
4321#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01004322 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004323
4324 codec->patch_ops = ad198x_patch_ops;
4325
4326 /* override some parameters */
4327 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
4328 ad1882_models, NULL);
4329 switch (board_config) {
4330 default:
4331 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004332 spec->num_mixers = 3;
4333 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004334 spec->channel_mode = ad1882_modes;
4335 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
4336 spec->need_dac_fix = 1;
4337 spec->multiout.max_channels = 2;
4338 spec->multiout.num_dacs = 1;
4339 break;
4340 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004341 spec->num_mixers = 3;
4342 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004343 break;
4344 }
4345 return 0;
4346}
4347
4348
4349/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004350 * patch entries
4351 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004352static struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004353 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02004354 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004355 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004356 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004357 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
4358 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02004359 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
4360 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004361 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004362 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01004363 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02004364 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004365 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02004366 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
4367 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004368 {} /* terminator */
4369};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004370
4371MODULE_ALIAS("snd-hda-codec-id:11d4*");
4372
4373MODULE_LICENSE("GPL");
4374MODULE_DESCRIPTION("Analog Devices HD-audio codec");
4375
4376static struct hda_codec_preset_list analog_list = {
4377 .preset = snd_hda_preset_analog,
4378 .owner = THIS_MODULE,
4379};
4380
4381static int __init patch_analog_init(void)
4382{
4383 return snd_hda_add_codec_preset(&analog_list);
4384}
4385
4386static void __exit patch_analog_exit(void)
4387{
4388 snd_hda_delete_codec_preset(&analog_list);
4389}
4390
4391module_init(patch_analog_init)
4392module_exit(patch_analog_exit)