blob: 9bcd8ab5a27f3cf9d2f81b80235e27c9976b9b93 [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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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{
Takashi Iwai2f334f92009-02-20 14:37:42 +01001050 unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001051 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1052}
1053
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054static int patch_ad1986a(struct hda_codec *codec)
1055{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001056 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001057 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001059 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 if (spec == NULL)
1061 return -ENOMEM;
1062
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 codec->spec = spec;
1064
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001065 err = snd_hda_attach_beep_device(codec, 0x19);
1066 if (err < 0) {
1067 ad198x_free(codec);
1068 return err;
1069 }
1070 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1071
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 spec->multiout.max_channels = 6;
1073 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1074 spec->multiout.dac_nids = ad1986a_dac_nids;
1075 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001076 spec->num_adc_nids = 1;
1077 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001078 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001079 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001080 spec->num_mixers = 1;
1081 spec->mixers[0] = ad1986a_mixers;
1082 spec->num_init_verbs = 1;
1083 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001084#ifdef CONFIG_SND_HDA_POWER_SAVE
1085 spec->loopback.amplist = ad1986a_loopbacks;
1086#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001087 spec->vmaster_nid = 0x1b;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001089 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090
Takashi Iwai9230d212006-03-13 13:49:49 +01001091 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001092 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1093 ad1986a_models,
1094 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001095 switch (board_config) {
1096 case AD1986A_3STACK:
1097 spec->num_mixers = 2;
1098 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001099 spec->num_init_verbs = 2;
1100 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001101 spec->channel_mode = ad1986a_modes;
1102 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001103 spec->need_dac_fix = 1;
1104 spec->multiout.max_channels = 2;
1105 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001106 break;
1107 case AD1986A_LAPTOP:
1108 spec->mixers[0] = ad1986a_laptop_mixers;
1109 spec->multiout.max_channels = 2;
1110 spec->multiout.num_dacs = 1;
1111 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1112 break;
Takashi Iwai825aa972006-03-17 10:50:49 +01001113 case AD1986A_LAPTOP_EAPD:
1114 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001115 spec->num_init_verbs = 2;
1116 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1117 spec->multiout.max_channels = 2;
1118 spec->multiout.num_dacs = 1;
1119 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1120 if (!is_jack_available(codec, 0x25))
1121 spec->multiout.dig_out_nid = 0;
1122 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1123 break;
1124 case AD1986A_SAMSUNG:
1125 spec->mixers[0] = ad1986a_samsung_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001126 spec->num_init_verbs = 3;
Takashi Iwai825aa972006-03-17 10:50:49 +01001127 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001128 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa972006-03-17 10:50:49 +01001129 spec->multiout.max_channels = 2;
1130 spec->multiout.num_dacs = 1;
1131 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001132 if (!is_jack_available(codec, 0x25))
1133 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001134 spec->input_mux = &ad1986a_automic_capture_source;
1135 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1136 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa972006-03-17 10:50:49 +01001137 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001138 case AD1986A_LAPTOP_AUTOMUTE:
1139 spec->mixers[0] = ad1986a_laptop_automute_mixers;
1140 spec->num_init_verbs = 3;
1141 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1142 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1143 spec->multiout.max_channels = 2;
1144 spec->multiout.num_dacs = 1;
1145 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001146 if (!is_jack_available(codec, 0x25))
1147 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001148 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1149 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1150 codec->patch_ops.init = ad1986a_hp_init;
1151 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001152 case AD1986A_ULTRA:
1153 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1154 spec->num_init_verbs = 2;
1155 spec->init_verbs[1] = ad1986a_ultra_init;
1156 spec->multiout.max_channels = 2;
1157 spec->multiout.num_dacs = 1;
1158 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1159 spec->multiout.dig_out_nid = 0;
1160 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001161 }
1162
Takashi Iwaid29240c2007-10-26 12:35:56 +02001163 /* AD1986A has a hardware problem that it can't share a stream
1164 * with multiple output pins. The copy of front to surrounds
1165 * causes noisy or silent outputs at a certain timing, e.g.
1166 * changing the volume.
1167 * So, let's disable the shared stream.
1168 */
1169 spec->multiout.no_share_stream = 1;
1170
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 return 0;
1172}
1173
1174/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001175 * AD1983 specific
1176 */
1177
1178#define AD1983_SPDIF_OUT 0x02
1179#define AD1983_DAC 0x03
1180#define AD1983_ADC 0x04
1181
1182static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001183static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001184static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001185
1186static struct hda_input_mux ad1983_capture_source = {
1187 .num_items = 4,
1188 .items = {
1189 { "Mic", 0x0 },
1190 { "Line", 0x1 },
1191 { "Mix", 0x2 },
1192 { "Mix Mono", 0x3 },
1193 },
1194};
1195
1196/*
1197 * SPDIF playback route
1198 */
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001199static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001200{
1201 static char *texts[] = { "PCM", "ADC" };
1202
1203 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1204 uinfo->count = 1;
1205 uinfo->value.enumerated.items = 2;
1206 if (uinfo->value.enumerated.item > 1)
1207 uinfo->value.enumerated.item = 1;
1208 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1209 return 0;
1210}
1211
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001212static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001213{
1214 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1215 struct ad198x_spec *spec = codec->spec;
1216
1217 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1218 return 0;
1219}
1220
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001221static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001222{
1223 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1224 struct ad198x_spec *spec = codec->spec;
1225
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001226 if (ucontrol->value.enumerated.item[0] > 1)
1227 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001228 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1229 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001230 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1231 AC_VERB_SET_CONNECT_SEL,
1232 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001233 return 1;
1234 }
1235 return 0;
1236}
1237
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001238static struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001239 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1240 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1241 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1242 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1243 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1244 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1245 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1246 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1247 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1248 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1249 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1250 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001251 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
1252 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1253 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1254 {
1255 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1256 .name = "Capture Source",
1257 .info = ad198x_mux_enum_info,
1258 .get = ad198x_mux_enum_get,
1259 .put = ad198x_mux_enum_put,
1260 },
1261 {
1262 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001263 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001264 .info = ad1983_spdif_route_info,
1265 .get = ad1983_spdif_route_get,
1266 .put = ad1983_spdif_route_put,
1267 },
1268 { } /* end */
1269};
1270
1271static struct hda_verb ad1983_init_verbs[] = {
1272 /* Front, HP, Mono; mute as default */
1273 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1274 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1275 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1276 /* Beep, PCM, Mic, Line-In: mute */
1277 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1278 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1279 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1280 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1281 /* Front, HP selectors; from Mix */
1282 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1283 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1284 /* Mono selector; from Mix */
1285 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1286 /* Mic selector; Mic */
1287 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1288 /* Line-in selector: Line-in */
1289 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1290 /* Mic boost: 0dB */
1291 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1292 /* Record selector: mic */
1293 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1294 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1295 /* SPDIF route: PCM */
1296 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1297 /* Front Pin */
1298 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1299 /* HP Pin */
1300 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1301 /* Mono Pin */
1302 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1303 /* Mic Pin */
1304 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1305 /* Line Pin */
1306 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1307 { } /* end */
1308};
1309
Takashi Iwaicb53c622007-08-10 17:21:45 +02001310#ifdef CONFIG_SND_HDA_POWER_SAVE
1311static struct hda_amp_list ad1983_loopbacks[] = {
1312 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1313 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1314 { } /* end */
1315};
1316#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001317
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001318static int patch_ad1983(struct hda_codec *codec)
1319{
1320 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001321 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001322
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001323 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001324 if (spec == NULL)
1325 return -ENOMEM;
1326
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001327 codec->spec = spec;
1328
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001329 err = snd_hda_attach_beep_device(codec, 0x10);
1330 if (err < 0) {
1331 ad198x_free(codec);
1332 return err;
1333 }
1334 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1335
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001336 spec->multiout.max_channels = 2;
1337 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1338 spec->multiout.dac_nids = ad1983_dac_nids;
1339 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001340 spec->num_adc_nids = 1;
1341 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001342 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001343 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001344 spec->num_mixers = 1;
1345 spec->mixers[0] = ad1983_mixers;
1346 spec->num_init_verbs = 1;
1347 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001348 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001349#ifdef CONFIG_SND_HDA_POWER_SAVE
1350 spec->loopback.amplist = ad1983_loopbacks;
1351#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001352 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001353
1354 codec->patch_ops = ad198x_patch_ops;
1355
1356 return 0;
1357}
1358
1359
1360/*
1361 * AD1981 HD specific
1362 */
1363
1364#define AD1981_SPDIF_OUT 0x02
1365#define AD1981_DAC 0x03
1366#define AD1981_ADC 0x04
1367
1368static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001369static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001370static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001371
1372/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
1373static struct hda_input_mux ad1981_capture_source = {
1374 .num_items = 7,
1375 .items = {
1376 { "Front Mic", 0x0 },
1377 { "Line", 0x1 },
1378 { "Mix", 0x2 },
1379 { "Mix Mono", 0x3 },
1380 { "CD", 0x4 },
1381 { "Mic", 0x6 },
1382 { "Aux", 0x7 },
1383 },
1384};
1385
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001386static struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001387 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1388 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1389 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1390 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1391 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1392 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1393 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1394 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1395 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1396 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1397 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1398 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1399 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1400 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1401 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1402 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1403 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1404 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001405 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
1406 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
1407 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1408 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1409 {
1410 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1411 .name = "Capture Source",
1412 .info = ad198x_mux_enum_info,
1413 .get = ad198x_mux_enum_get,
1414 .put = ad198x_mux_enum_put,
1415 },
1416 /* identical with AD1983 */
1417 {
1418 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001419 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001420 .info = ad1983_spdif_route_info,
1421 .get = ad1983_spdif_route_get,
1422 .put = ad1983_spdif_route_put,
1423 },
1424 { } /* end */
1425};
1426
1427static struct hda_verb ad1981_init_verbs[] = {
1428 /* Front, HP, Mono; mute as default */
1429 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1430 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1431 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1432 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1433 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1434 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1435 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1436 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1437 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1438 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1439 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1440 /* Front, HP selectors; from Mix */
1441 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1442 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1443 /* Mono selector; from Mix */
1444 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1445 /* Mic Mixer; select Front Mic */
1446 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1447 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1448 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001449 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1450 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001451 /* Record selector: Front mic */
1452 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1453 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1454 /* SPDIF route: PCM */
1455 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1456 /* Front Pin */
1457 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1458 /* HP Pin */
1459 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1460 /* Mono Pin */
1461 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1462 /* Front & Rear Mic Pins */
1463 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1464 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1465 /* Line Pin */
1466 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1467 /* Digital Beep */
1468 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1469 /* Line-Out as Input: disabled */
1470 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1471 { } /* end */
1472};
1473
Takashi Iwaicb53c622007-08-10 17:21:45 +02001474#ifdef CONFIG_SND_HDA_POWER_SAVE
1475static struct hda_amp_list ad1981_loopbacks[] = {
1476 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1477 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1478 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1479 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1480 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1481 { } /* end */
1482};
1483#endif
1484
Takashi Iwai18a815d2006-03-01 19:54:39 +01001485/*
1486 * Patch for HP nx6320
1487 *
Tobin Davis18768992007-03-12 22:20:51 +01001488 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001489 * speaker output enabled _and_ mute-LED off.
1490 */
1491
1492#define AD1981_HP_EVENT 0x37
1493#define AD1981_MIC_EVENT 0x38
1494
1495static struct hda_verb ad1981_hp_init_verbs[] = {
1496 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1497 /* pin sensing on HP and Mic jacks */
1498 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1499 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1500 {}
1501};
1502
1503/* turn on/off EAPD (+ mute HP) as a master switch */
1504static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1505 struct snd_ctl_elem_value *ucontrol)
1506{
1507 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1508 struct ad198x_spec *spec = codec->spec;
1509
1510 if (! ad198x_eapd_put(kcontrol, ucontrol))
1511 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001512 /* change speaker pin appropriately */
1513 snd_hda_codec_write(codec, 0x05, 0,
1514 AC_VERB_SET_PIN_WIDGET_CONTROL,
1515 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001516 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001517 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1518 HDA_AMP_MUTE,
1519 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001520 return 1;
1521}
1522
1523/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02001524static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
1525 .ops = &snd_hda_bind_vol,
1526 .values = {
1527 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1528 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1529 0
1530 },
1531};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001532
1533/* mute internal speaker if HP is plugged */
1534static void ad1981_hp_automute(struct hda_codec *codec)
1535{
1536 unsigned int present;
1537
1538 present = snd_hda_codec_read(codec, 0x06, 0,
1539 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
Takashi Iwai47fd8302007-08-10 17:11:07 +02001540 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1541 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001542}
1543
1544/* toggle input of built-in and mic jack appropriately */
1545static void ad1981_hp_automic(struct hda_codec *codec)
1546{
1547 static struct hda_verb mic_jack_on[] = {
1548 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1549 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1550 {}
1551 };
1552 static struct hda_verb mic_jack_off[] = {
1553 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1554 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1555 {}
1556 };
1557 unsigned int present;
1558
1559 present = snd_hda_codec_read(codec, 0x08, 0,
1560 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1561 if (present)
1562 snd_hda_sequence_write(codec, mic_jack_on);
1563 else
1564 snd_hda_sequence_write(codec, mic_jack_off);
1565}
1566
1567/* unsolicited event for HP jack sensing */
1568static void ad1981_hp_unsol_event(struct hda_codec *codec,
1569 unsigned int res)
1570{
1571 res >>= 26;
1572 switch (res) {
1573 case AD1981_HP_EVENT:
1574 ad1981_hp_automute(codec);
1575 break;
1576 case AD1981_MIC_EVENT:
1577 ad1981_hp_automic(codec);
1578 break;
1579 }
1580}
1581
1582static struct hda_input_mux ad1981_hp_capture_source = {
1583 .num_items = 3,
1584 .items = {
1585 { "Mic", 0x0 },
1586 { "Docking-Station", 0x1 },
1587 { "Mix", 0x2 },
1588 },
1589};
1590
1591static struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001592 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001593 {
1594 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1595 .name = "Master Playback Switch",
1596 .info = ad198x_eapd_info,
1597 .get = ad198x_eapd_get,
1598 .put = ad1981_hp_master_sw_put,
1599 .private_value = 0x05,
1600 },
1601 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1602 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1603#if 0
1604 /* FIXME: analog mic/line loopback doesn't work with my tests...
1605 * (although recording is OK)
1606 */
1607 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1608 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1609 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1610 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1611 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1612 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1613 /* FIXME: does this laptop have analog CD connection? */
1614 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1615 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1616#endif
1617 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1618 HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
1619 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1620 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1621 {
1622 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1623 .name = "Capture Source",
1624 .info = ad198x_mux_enum_info,
1625 .get = ad198x_mux_enum_get,
1626 .put = ad198x_mux_enum_put,
1627 },
1628 { } /* end */
1629};
1630
1631/* initialize jack-sensing, too */
1632static int ad1981_hp_init(struct hda_codec *codec)
1633{
1634 ad198x_init(codec);
1635 ad1981_hp_automute(codec);
1636 ad1981_hp_automic(codec);
1637 return 0;
1638}
1639
Tobin Davis18768992007-03-12 22:20:51 +01001640/* configuration for Toshiba Laptops */
1641static struct hda_verb ad1981_toshiba_init_verbs[] = {
1642 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1643 /* pin sensing on HP and Mic jacks */
1644 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1645 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1646 {}
1647};
1648
1649static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
1650 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1651 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1652 { }
1653};
1654
Takashi Iwai01686c52006-04-18 12:54:11 +02001655/* configuration for Lenovo Thinkpad T60 */
1656static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
1657 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1658 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1659 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1660 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1661 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1662 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1663 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1664 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1665 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1666 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1667 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1668 {
1669 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1670 .name = "Capture Source",
1671 .info = ad198x_mux_enum_info,
1672 .get = ad198x_mux_enum_get,
1673 .put = ad198x_mux_enum_put,
1674 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001675 /* identical with AD1983 */
1676 {
1677 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1678 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1679 .info = ad1983_spdif_route_info,
1680 .get = ad1983_spdif_route_get,
1681 .put = ad1983_spdif_route_put,
1682 },
Takashi Iwai01686c52006-04-18 12:54:11 +02001683 { } /* end */
1684};
1685
1686static struct hda_input_mux ad1981_thinkpad_capture_source = {
1687 .num_items = 3,
1688 .items = {
1689 { "Mic", 0x0 },
1690 { "Mix", 0x2 },
1691 { "CD", 0x4 },
1692 },
1693};
1694
Takashi Iwai18a815d2006-03-01 19:54:39 +01001695/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001696enum {
1697 AD1981_BASIC,
1698 AD1981_HP,
1699 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001700 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001701 AD1981_MODELS
1702};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001703
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001704static const char *ad1981_models[AD1981_MODELS] = {
1705 [AD1981_HP] = "hp",
1706 [AD1981_THINKPAD] = "thinkpad",
1707 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001708 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001709};
1710
1711static struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001712 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001713 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001714 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001715 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001716 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c52006-04-18 12:54:11 +02001717 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001718 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001719 /* HP nx6320 (reversed SSID, H/W bug) */
1720 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001721 {}
1722};
1723
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001724static int patch_ad1981(struct hda_codec *codec)
1725{
1726 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001727 int err, board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001728
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001729 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001730 if (spec == NULL)
1731 return -ENOMEM;
1732
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001733 codec->spec = spec;
1734
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001735 err = snd_hda_attach_beep_device(codec, 0x10);
1736 if (err < 0) {
1737 ad198x_free(codec);
1738 return err;
1739 }
1740 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
1741
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001742 spec->multiout.max_channels = 2;
1743 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1744 spec->multiout.dac_nids = ad1981_dac_nids;
1745 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001746 spec->num_adc_nids = 1;
1747 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001748 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001749 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001750 spec->num_mixers = 1;
1751 spec->mixers[0] = ad1981_mixers;
1752 spec->num_init_verbs = 1;
1753 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001754 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001755#ifdef CONFIG_SND_HDA_POWER_SAVE
1756 spec->loopback.amplist = ad1981_loopbacks;
1757#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001758 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001759
1760 codec->patch_ops = ad198x_patch_ops;
1761
Takashi Iwai18a815d2006-03-01 19:54:39 +01001762 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001763 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1764 ad1981_models,
1765 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001766 switch (board_config) {
1767 case AD1981_HP:
1768 spec->mixers[0] = ad1981_hp_mixers;
1769 spec->num_init_verbs = 2;
1770 spec->init_verbs[1] = ad1981_hp_init_verbs;
1771 spec->multiout.dig_out_nid = 0;
1772 spec->input_mux = &ad1981_hp_capture_source;
1773
1774 codec->patch_ops.init = ad1981_hp_init;
1775 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1776 break;
Takashi Iwai01686c52006-04-18 12:54:11 +02001777 case AD1981_THINKPAD:
1778 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c52006-04-18 12:54:11 +02001779 spec->input_mux = &ad1981_thinkpad_capture_source;
1780 break;
Tobin Davis18768992007-03-12 22:20:51 +01001781 case AD1981_TOSHIBA:
1782 spec->mixers[0] = ad1981_hp_mixers;
1783 spec->mixers[1] = ad1981_toshiba_mixers;
1784 spec->num_init_verbs = 2;
1785 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1786 spec->multiout.dig_out_nid = 0;
1787 spec->input_mux = &ad1981_hp_capture_source;
1788 codec->patch_ops.init = ad1981_hp_init;
1789 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1790 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001791 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001792 return 0;
1793}
1794
1795
1796/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001797 * AD1988
1798 *
1799 * Output pins and routes
1800 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001801 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001802 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1803 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1804 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1805 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1806 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1807 * port-F 0x16 (mute) <- 0x2a <- 06
1808 * port-G 0x24 (mute) <- 0x27 <- 05
1809 * port-H 0x25 (mute) <- 0x28 <- 0a
1810 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1811 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001812 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1813 * (*) 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 +01001814 *
1815 * Input pins and routes
1816 *
1817 * pin boost mix input # / adc input #
1818 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1819 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1820 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1821 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1822 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1823 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1824 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1825 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1826 *
1827 *
1828 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001829 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001830 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001831 *
1832 * Inputs of Analog Mix (0x20)
1833 * 0:Port-B (front mic)
1834 * 1:Port-C/G/H (line-in)
1835 * 2:Port-A
1836 * 3:Port-D (line-in/2)
1837 * 4:Port-E/G/H (mic-in)
1838 * 5:Port-F (mic2-in)
1839 * 6:CD
1840 * 7:Beep
1841 *
1842 * ADC selection
1843 * 0:Port-A
1844 * 1:Port-B (front mic-in)
1845 * 2:Port-C (line-in)
1846 * 3:Port-F (mic2-in)
1847 * 4:Port-E (mic-in)
1848 * 5:CD
1849 * 6:Port-G
1850 * 7:Port-H
1851 * 8:Port-D (line-in/2)
1852 * 9:Mix
1853 *
1854 * Proposed pin assignments by the datasheet
1855 *
1856 * 6-stack
1857 * Port-A front headphone
1858 * B front mic-in
1859 * C rear line-in
1860 * D rear front-out
1861 * E rear mic-in
1862 * F rear surround
1863 * G rear CLFE
1864 * H rear side
1865 *
1866 * 3-stack
1867 * Port-A front headphone
1868 * B front mic
1869 * C rear line-in/surround
1870 * D rear front-out
1871 * E rear mic-in/CLFE
1872 *
1873 * laptop
1874 * Port-A headphone
1875 * B mic-in
1876 * C docking station
1877 * D internal speaker (with EAPD)
1878 * E/F quad mic array
1879 */
1880
1881
1882/* models */
1883enum {
1884 AD1988_6STACK,
1885 AD1988_6STACK_DIG,
1886 AD1988_3STACK,
1887 AD1988_3STACK_DIG,
1888 AD1988_LAPTOP,
1889 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01001890 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001891 AD1988_MODEL_LAST,
1892};
1893
Takashi Iwaid32410b12005-11-24 16:06:23 +01001894/* reivision id to check workarounds */
1895#define AD1988A_REV2 0x100200
1896
Takashi Iwai1a806f42006-07-03 15:58:16 +02001897#define is_rev2(codec) \
1898 ((codec)->vendor_id == 0x11d41988 && \
1899 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001900
1901/*
1902 * mixers
1903 */
1904
Takashi Iwaid32410b12005-11-24 16:06:23 +01001905static hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001906 0x04, 0x06, 0x05, 0x0a
1907};
1908
Takashi Iwaid32410b12005-11-24 16:06:23 +01001909static hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001910 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01001911};
1912
1913/* for AD1988A revision-2, DAC2-4 are swapped */
1914static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
1915 0x04, 0x05, 0x0a, 0x06
1916};
1917
1918static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001919 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01001920};
1921
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001922static hda_nid_t ad1988_adc_nids[3] = {
1923 0x08, 0x09, 0x0f
1924};
1925
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001926static hda_nid_t ad1988_capsrc_nids[3] = {
1927 0x0c, 0x0d, 0x0e
1928};
1929
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001930#define AD1988_SPDIF_OUT 0x02
1931#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001932#define AD1988_SPDIF_IN 0x07
1933
Takashi Iwai3a08e302009-02-13 11:37:08 +01001934static hda_nid_t ad1989b_slave_dig_outs[] = {
1935 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001936};
1937
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001938static struct hda_input_mux ad1988_6stack_capture_source = {
1939 .num_items = 5,
1940 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001941 { "Front Mic", 0x1 }, /* port-B */
1942 { "Line", 0x2 }, /* port-C */
1943 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001944 { "CD", 0x5 },
1945 { "Mix", 0x9 },
1946 },
1947};
1948
1949static struct hda_input_mux ad1988_laptop_capture_source = {
1950 .num_items = 3,
1951 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001952 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001953 { "CD", 0x5 },
1954 { "Mix", 0x9 },
1955 },
1956};
1957
1958/*
1959 */
1960static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
1961 struct snd_ctl_elem_info *uinfo)
1962{
1963 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1964 struct ad198x_spec *spec = codec->spec;
1965 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
1966 spec->num_channel_mode);
1967}
1968
1969static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
1970 struct snd_ctl_elem_value *ucontrol)
1971{
1972 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1973 struct ad198x_spec *spec = codec->spec;
1974 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
1975 spec->num_channel_mode, spec->multiout.max_channels);
1976}
1977
1978static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
1979 struct snd_ctl_elem_value *ucontrol)
1980{
1981 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1982 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001983 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
1984 spec->num_channel_mode,
1985 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02001986 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02001987 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001988 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001989}
1990
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001991/* 6-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001992static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001993 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1994 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1995 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
1996 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
1997 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001998 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001999};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002000
Takashi Iwaid32410b12005-11-24 16:06:23 +01002001static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
2002 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2003 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2004 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2005 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2006 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002007 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002008};
2009
2010static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002011 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2012 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2013 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2014 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2015 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2016 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2017 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2018
2019 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2020 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2021 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2022 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2023 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2024 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2025 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2026 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2027
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002028 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002029 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2030
2031 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2032 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2033
2034 { } /* end */
2035};
2036
2037/* 3-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002038static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002039 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002040 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002041 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2042 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002043 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002044};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002045
Takashi Iwaid32410b12005-11-24 16:06:23 +01002046static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
2047 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002048 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2049 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2050 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002051 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002052};
2053
2054static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002055 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002056 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2057 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2058 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002059 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2060 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2061
2062 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2063 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2064 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2065 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2066 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2067 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2068 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2069 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2070
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002071 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002072 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2073
2074 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2075 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2076 {
2077 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2078 .name = "Channel Mode",
2079 .info = ad198x_ch_mode_info,
2080 .get = ad198x_ch_mode_get,
2081 .put = ad198x_ch_mode_put,
2082 },
2083
2084 { } /* end */
2085};
2086
2087/* laptop mode */
2088static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
2089 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2090 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2091 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2092
2093 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2094 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2095 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2096 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2097 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2098 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2099
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002100 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002101 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2102
2103 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2104
2105 {
2106 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2107 .name = "External Amplifier",
Takashi Iwai18a815d2006-03-01 19:54:39 +01002108 .info = ad198x_eapd_info,
2109 .get = ad198x_eapd_get,
2110 .put = ad198x_eapd_put,
2111 .private_value = 0x12 | (1 << 8), /* port-D, inversed */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002112 },
2113
2114 { } /* end */
2115};
2116
2117/* capture */
2118static struct snd_kcontrol_new ad1988_capture_mixers[] = {
2119 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2120 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2121 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2122 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2123 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2124 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2125 {
2126 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2127 /* The multiple "Capture Source" controls confuse alsamixer
2128 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002129 */
2130 /* .name = "Capture Source", */
2131 .name = "Input Source",
2132 .count = 3,
2133 .info = ad198x_mux_enum_info,
2134 .get = ad198x_mux_enum_get,
2135 .put = ad198x_mux_enum_put,
2136 },
2137 { } /* end */
2138};
2139
2140static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2141 struct snd_ctl_elem_info *uinfo)
2142{
2143 static char *texts[] = {
2144 "PCM", "ADC1", "ADC2", "ADC3"
2145 };
2146 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2147 uinfo->count = 1;
2148 uinfo->value.enumerated.items = 4;
2149 if (uinfo->value.enumerated.item >= 4)
2150 uinfo->value.enumerated.item = 3;
2151 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2152 return 0;
2153}
2154
2155static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2156 struct snd_ctl_elem_value *ucontrol)
2157{
2158 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2159 unsigned int sel;
2160
Takashi Iwaibddcf542007-07-24 18:04:05 +02002161 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2162 AC_AMP_GET_INPUT);
2163 if (!(sel & 0x80))
2164 ucontrol->value.enumerated.item[0] = 0;
2165 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002166 sel = snd_hda_codec_read(codec, 0x0b, 0,
2167 AC_VERB_GET_CONNECT_SEL, 0);
2168 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002169 sel++;
2170 else
2171 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002172 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002173 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002174 return 0;
2175}
2176
2177static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2178 struct snd_ctl_elem_value *ucontrol)
2179{
2180 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002181 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002182 int change;
2183
Takashi Iwai35b26722007-05-05 12:17:17 +02002184 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002185 if (val > 3)
2186 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002187 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002188 sel = snd_hda_codec_read(codec, 0x1d, 0,
2189 AC_VERB_GET_AMP_GAIN_MUTE,
2190 AC_AMP_GET_INPUT);
2191 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002192 if (change) {
2193 snd_hda_codec_write_cache(codec, 0x1d, 0,
2194 AC_VERB_SET_AMP_GAIN_MUTE,
2195 AMP_IN_UNMUTE(0));
2196 snd_hda_codec_write_cache(codec, 0x1d, 0,
2197 AC_VERB_SET_AMP_GAIN_MUTE,
2198 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002199 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002200 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002201 sel = snd_hda_codec_read(codec, 0x1d, 0,
2202 AC_VERB_GET_AMP_GAIN_MUTE,
2203 AC_AMP_GET_INPUT | 0x01);
2204 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002205 if (change) {
2206 snd_hda_codec_write_cache(codec, 0x1d, 0,
2207 AC_VERB_SET_AMP_GAIN_MUTE,
2208 AMP_IN_MUTE(0));
2209 snd_hda_codec_write_cache(codec, 0x1d, 0,
2210 AC_VERB_SET_AMP_GAIN_MUTE,
2211 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002212 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002213 sel = snd_hda_codec_read(codec, 0x0b, 0,
2214 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2215 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002216 if (change)
2217 snd_hda_codec_write_cache(codec, 0x0b, 0,
2218 AC_VERB_SET_CONNECT_SEL,
2219 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002220 }
2221 return change;
2222}
2223
2224static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
2225 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2226 {
2227 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2228 .name = "IEC958 Playback Source",
2229 .info = ad1988_spdif_playback_source_info,
2230 .get = ad1988_spdif_playback_source_get,
2231 .put = ad1988_spdif_playback_source_put,
2232 },
2233 { } /* end */
2234};
2235
2236static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
2237 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2238 { } /* end */
2239};
2240
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002241static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
2242 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002243 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002244 { } /* end */
2245};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002246
2247/*
2248 * initialization verbs
2249 */
2250
2251/*
2252 * for 6-stack (+dig)
2253 */
2254static struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002255 /* Front, Surround, CLFE, side DAC; unmute as default */
2256 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2257 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2258 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2259 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002260 /* Port-A front headphon path */
2261 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2262 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2263 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2264 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2265 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2266 /* Port-D line-out path */
2267 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2268 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2269 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2270 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2271 /* Port-F surround path */
2272 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2273 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2274 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2275 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2276 /* Port-G CLFE path */
2277 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2278 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2279 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2280 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2281 /* Port-H side path */
2282 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2283 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2284 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2285 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2286 /* Mono out path */
2287 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2288 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2289 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2290 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2291 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2292 /* Port-B front mic-in path */
2293 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2294 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2295 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2296 /* Port-C line-in path */
2297 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2298 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2299 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2300 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2301 /* Port-E mic-in path */
2302 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2303 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2304 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2305 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002306 /* Analog CD Input */
2307 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002308 /* Analog Mix output amp */
2309 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002310
2311 { }
2312};
2313
2314static struct hda_verb ad1988_capture_init_verbs[] = {
2315 /* mute analog mix */
2316 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2317 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2318 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2319 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2320 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2321 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2322 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2323 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2324 /* select ADCs - front-mic */
2325 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2326 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2327 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002328
2329 { }
2330};
2331
2332static struct hda_verb ad1988_spdif_init_verbs[] = {
2333 /* SPDIF out sel */
2334 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2335 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2336 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002337 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002338 /* SPDIF out pin */
2339 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002340
2341 { }
2342};
2343
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002344/* AD1989 has no ADC -> SPDIF route */
2345static struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002346 /* SPDIF-1 out pin */
2347 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002348 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002349 /* SPDIF-2/HDMI out pin */
2350 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2351 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002352 { }
2353};
2354
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002355/*
2356 * verbs for 3stack (+dig)
2357 */
2358static struct hda_verb ad1988_3stack_ch2_init[] = {
2359 /* set port-C to line-in */
2360 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2361 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2362 /* set port-E to mic-in */
2363 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2364 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2365 { } /* end */
2366};
2367
2368static struct hda_verb ad1988_3stack_ch6_init[] = {
2369 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002370 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002371 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002372 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002373 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002374 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002375 { } /* end */
2376};
2377
2378static struct hda_channel_mode ad1988_3stack_modes[2] = {
2379 { 2, ad1988_3stack_ch2_init },
2380 { 6, ad1988_3stack_ch6_init },
2381};
2382
2383static struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002384 /* Front, Surround, CLFE, side DAC; unmute as default */
2385 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2386 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2387 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2388 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002389 /* Port-A front headphon path */
2390 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2391 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2392 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2393 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2394 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2395 /* Port-D line-out path */
2396 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2397 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2398 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2399 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2400 /* Mono out path */
2401 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2402 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2403 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2404 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2405 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2406 /* Port-B front mic-in path */
2407 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2408 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2409 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002410 /* Port-C line-in/surround path - 6ch mode as default */
2411 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2412 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002413 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002414 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002415 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002416 /* Port-E mic-in/CLFE path - 6ch mode as default */
2417 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2418 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002419 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002420 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002421 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2422 /* mute analog mix */
2423 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2424 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2425 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2426 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2427 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2428 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2429 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2430 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2431 /* select ADCs - front-mic */
2432 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2433 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2434 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002435 /* Analog Mix output amp */
2436 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002437 { }
2438};
2439
2440/*
2441 * verbs for laptop mode (+dig)
2442 */
2443static struct hda_verb ad1988_laptop_hp_on[] = {
2444 /* unmute port-A and mute port-D */
2445 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2446 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2447 { } /* end */
2448};
2449static struct hda_verb ad1988_laptop_hp_off[] = {
2450 /* mute port-A and unmute port-D */
2451 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2452 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2453 { } /* end */
2454};
2455
2456#define AD1988_HP_EVENT 0x01
2457
2458static struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002459 /* Front, Surround, CLFE, side DAC; unmute as default */
2460 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2461 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2462 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2463 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002464 /* Port-A front headphon path */
2465 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2466 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2467 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2468 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2469 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2470 /* unsolicited event for pin-sense */
2471 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2472 /* Port-D line-out path + EAPD */
2473 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2474 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2475 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2476 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2477 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2478 /* Mono out path */
2479 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2480 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2481 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2482 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2483 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2484 /* Port-B mic-in path */
2485 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2486 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2487 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2488 /* Port-C docking station - try to output */
2489 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2490 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2491 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2492 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2493 /* mute analog mix */
2494 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2495 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2496 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2497 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2498 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2499 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2500 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2501 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2502 /* select ADCs - mic */
2503 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2504 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2505 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002506 /* Analog Mix output amp */
2507 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002508 { }
2509};
2510
2511static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2512{
2513 if ((res >> 26) != AD1988_HP_EVENT)
2514 return;
2515 if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31))
2516 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2517 else
2518 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2519}
2520
Takashi Iwaicb53c622007-08-10 17:21:45 +02002521#ifdef CONFIG_SND_HDA_POWER_SAVE
2522static struct hda_amp_list ad1988_loopbacks[] = {
2523 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2524 { 0x20, HDA_INPUT, 1 }, /* Line */
2525 { 0x20, HDA_INPUT, 4 }, /* Mic */
2526 { 0x20, HDA_INPUT, 6 }, /* CD */
2527 { } /* end */
2528};
2529#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002530
2531/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002532 * Automatic parse of I/O pins from the BIOS configuration
2533 */
2534
Takashi Iwaid32410b12005-11-24 16:06:23 +01002535enum {
2536 AD_CTL_WIDGET_VOL,
2537 AD_CTL_WIDGET_MUTE,
2538 AD_CTL_BIND_MUTE,
2539};
2540static struct snd_kcontrol_new ad1988_control_templates[] = {
2541 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2542 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2543 HDA_BIND_MUTE(NULL, 0, 0, 0),
2544};
2545
2546/* add dynamic controls */
2547static int add_control(struct ad198x_spec *spec, int type, const char *name,
2548 unsigned long val)
2549{
2550 struct snd_kcontrol_new *knew;
2551
Takashi Iwai603c4012008-07-30 15:01:44 +02002552 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2553 knew = snd_array_new(&spec->kctls);
2554 if (!knew)
2555 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002556 *knew = ad1988_control_templates[type];
2557 knew->name = kstrdup(name, GFP_KERNEL);
2558 if (! knew->name)
2559 return -ENOMEM;
2560 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002561 return 0;
2562}
2563
2564#define AD1988_PIN_CD_NID 0x18
2565#define AD1988_PIN_BEEP_NID 0x10
2566
2567static hda_nid_t ad1988_mixer_nids[8] = {
2568 /* A B C D E F G H */
2569 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2570};
2571
2572static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2573{
2574 static hda_nid_t idx_to_dac[8] = {
2575 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002576 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002577 };
2578 static hda_nid_t idx_to_dac_rev2[8] = {
2579 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002580 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002581 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002582 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002583 return idx_to_dac_rev2[idx];
2584 else
2585 return idx_to_dac[idx];
2586}
2587
2588static hda_nid_t ad1988_boost_nids[8] = {
2589 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2590};
2591
2592static int ad1988_pin_idx(hda_nid_t nid)
2593{
2594 static hda_nid_t ad1988_io_pins[8] = {
2595 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2596 };
2597 int i;
2598 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2599 if (ad1988_io_pins[i] == nid)
2600 return i;
2601 return 0; /* should be -1 */
2602}
2603
2604static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2605{
2606 static int loopback_idx[8] = {
2607 2, 0, 1, 3, 4, 5, 1, 4
2608 };
2609 switch (nid) {
2610 case AD1988_PIN_CD_NID:
2611 return 6;
2612 default:
2613 return loopback_idx[ad1988_pin_idx(nid)];
2614 }
2615}
2616
2617static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2618{
2619 static int adc_idx[8] = {
2620 0, 1, 2, 8, 4, 3, 6, 7
2621 };
2622 switch (nid) {
2623 case AD1988_PIN_CD_NID:
2624 return 5;
2625 default:
2626 return adc_idx[ad1988_pin_idx(nid)];
2627 }
2628}
2629
2630/* fill in the dac_nids table from the parsed pin configuration */
2631static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2632 const struct auto_pin_cfg *cfg)
2633{
2634 struct ad198x_spec *spec = codec->spec;
2635 int i, idx;
2636
2637 spec->multiout.dac_nids = spec->private_dac_nids;
2638
2639 /* check the pins hardwired to audio widget */
2640 for (i = 0; i < cfg->line_outs; i++) {
2641 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2642 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2643 }
2644 spec->multiout.num_dacs = cfg->line_outs;
2645 return 0;
2646}
2647
2648/* add playback controls from the parsed DAC table */
2649static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2650 const struct auto_pin_cfg *cfg)
2651{
2652 char name[32];
2653 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2654 hda_nid_t nid;
2655 int i, err;
2656
2657 for (i = 0; i < cfg->line_outs; i++) {
2658 hda_nid_t dac = spec->multiout.dac_nids[i];
2659 if (! dac)
2660 continue;
2661 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2662 if (i == 2) {
2663 /* Center/LFE */
2664 err = add_control(spec, AD_CTL_WIDGET_VOL,
2665 "Center Playback Volume",
2666 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2667 if (err < 0)
2668 return err;
2669 err = add_control(spec, AD_CTL_WIDGET_VOL,
2670 "LFE Playback Volume",
2671 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2672 if (err < 0)
2673 return err;
2674 err = add_control(spec, AD_CTL_BIND_MUTE,
2675 "Center Playback Switch",
2676 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2677 if (err < 0)
2678 return err;
2679 err = add_control(spec, AD_CTL_BIND_MUTE,
2680 "LFE Playback Switch",
2681 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2682 if (err < 0)
2683 return err;
2684 } else {
2685 sprintf(name, "%s Playback Volume", chname[i]);
2686 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2687 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2688 if (err < 0)
2689 return err;
2690 sprintf(name, "%s Playback Switch", chname[i]);
2691 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2692 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2693 if (err < 0)
2694 return err;
2695 }
2696 }
2697 return 0;
2698}
2699
2700/* add playback controls for speaker and HP outputs */
2701static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2702 const char *pfx)
2703{
2704 struct ad198x_spec *spec = codec->spec;
2705 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02002706 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002707 char name[32];
2708
2709 if (! pin)
2710 return 0;
2711
2712 idx = ad1988_pin_idx(pin);
2713 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02002714 /* check whether the corresponding DAC was already taken */
2715 for (i = 0; i < spec->autocfg.line_outs; i++) {
2716 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2717 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
2718 if (dac == nid)
2719 break;
2720 }
2721 if (i >= spec->autocfg.line_outs) {
2722 /* specify the DAC as the extra output */
2723 if (!spec->multiout.hp_nid)
2724 spec->multiout.hp_nid = nid;
2725 else
2726 spec->multiout.extra_out_nid[0] = nid;
2727 /* control HP volume/switch on the output mixer amp */
2728 sprintf(name, "%s Playback Volume", pfx);
2729 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2730 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2731 if (err < 0)
2732 return err;
2733 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01002734 nid = ad1988_mixer_nids[idx];
2735 sprintf(name, "%s Playback Switch", pfx);
2736 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2737 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2738 return err;
2739 return 0;
2740}
2741
2742/* create input playback/capture controls for the given pin */
2743static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2744 const char *ctlname, int boost)
2745{
2746 char name[32];
2747 int err, idx;
2748
2749 sprintf(name, "%s Playback Volume", ctlname);
2750 idx = ad1988_pin_to_loopback_idx(pin);
2751 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2752 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2753 return err;
2754 sprintf(name, "%s Playback Switch", ctlname);
2755 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2756 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2757 return err;
2758 if (boost) {
2759 hda_nid_t bnid;
2760 idx = ad1988_pin_idx(pin);
2761 bnid = ad1988_boost_nids[idx];
2762 if (bnid) {
2763 sprintf(name, "%s Boost", ctlname);
2764 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2765 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2766
2767 }
2768 }
2769 return 0;
2770}
2771
2772/* create playback/capture controls for input pins */
2773static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2774 const struct auto_pin_cfg *cfg)
2775{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002776 struct hda_input_mux *imux = &spec->private_imux;
2777 int i, err;
2778
2779 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002780 err = new_analog_input(spec, cfg->input_pins[i],
2781 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002782 i <= AUTO_PIN_FRONT_MIC);
2783 if (err < 0)
2784 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002785 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002786 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2787 imux->num_items++;
2788 }
2789 imux->items[imux->num_items].label = "Mix";
2790 imux->items[imux->num_items].index = 9;
2791 imux->num_items++;
2792
2793 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2794 "Analog Mix Playback Volume",
2795 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2796 return err;
2797 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2798 "Analog Mix Playback Switch",
2799 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2800 return err;
2801
2802 return 0;
2803}
2804
2805static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2806 hda_nid_t nid, int pin_type,
2807 int dac_idx)
2808{
2809 /* set as output */
2810 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2811 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2812 switch (nid) {
2813 case 0x11: /* port-A - DAC 04 */
2814 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2815 break;
2816 case 0x14: /* port-B - DAC 06 */
2817 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2818 break;
2819 case 0x15: /* port-C - DAC 05 */
2820 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2821 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002822 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002823 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2824 break;
2825 case 0x13: /* mono - DAC 04 */
2826 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2827 break;
2828 }
2829}
2830
2831static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2832{
2833 struct ad198x_spec *spec = codec->spec;
2834 int i;
2835
2836 for (i = 0; i < spec->autocfg.line_outs; i++) {
2837 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2838 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2839 }
2840}
2841
2842static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2843{
2844 struct ad198x_spec *spec = codec->spec;
2845 hda_nid_t pin;
2846
Takashi Iwai82bc9552006-03-21 11:24:42 +01002847 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002848 if (pin) /* connect to front */
2849 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002850 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002851 if (pin) /* connect to front */
2852 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2853}
2854
2855static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2856{
2857 struct ad198x_spec *spec = codec->spec;
2858 int i, idx;
2859
2860 for (i = 0; i < AUTO_PIN_LAST; i++) {
2861 hda_nid_t nid = spec->autocfg.input_pins[i];
2862 if (! nid)
2863 continue;
2864 switch (nid) {
2865 case 0x15: /* port-C */
2866 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2867 break;
2868 case 0x17: /* port-E */
2869 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2870 break;
2871 }
2872 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2873 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2874 if (nid != AD1988_PIN_CD_NID)
2875 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
2876 AMP_OUT_MUTE);
2877 idx = ad1988_pin_idx(nid);
2878 if (ad1988_boost_nids[idx])
2879 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
2880 AC_VERB_SET_AMP_GAIN_MUTE,
2881 AMP_OUT_ZERO);
2882 }
2883}
2884
2885/* parse the BIOS configuration and set up the alc_spec */
2886/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
2887static int ad1988_parse_auto_config(struct hda_codec *codec)
2888{
2889 struct ad198x_spec *spec = codec->spec;
2890 int err;
2891
Kailang Yangdf694da2005-12-05 19:42:22 +01002892 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002893 return err;
2894 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
2895 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002896 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002897 return 0; /* can't find valid BIOS pin config */
2898 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01002899 (err = ad1988_auto_create_extra_out(codec,
2900 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002901 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002902 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002903 "Headphone")) < 0 ||
2904 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
2905 return err;
2906
2907 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2908
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002909 if (spec->autocfg.dig_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002910 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2911 if (spec->autocfg.dig_in_pin)
2912 spec->dig_in_nid = AD1988_SPDIF_IN;
2913
Takashi Iwai603c4012008-07-30 15:01:44 +02002914 if (spec->kctls.list)
2915 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002916
2917 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
2918
2919 spec->input_mux = &spec->private_imux;
2920
2921 return 1;
2922}
2923
2924/* init callback for auto-configuration model -- overriding the default init */
2925static int ad1988_auto_init(struct hda_codec *codec)
2926{
2927 ad198x_init(codec);
2928 ad1988_auto_init_multi_out(codec);
2929 ad1988_auto_init_extra_out(codec);
2930 ad1988_auto_init_analog_input(codec);
2931 return 0;
2932}
2933
2934
2935/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002936 */
2937
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002938static const char *ad1988_models[AD1988_MODEL_LAST] = {
2939 [AD1988_6STACK] = "6stack",
2940 [AD1988_6STACK_DIG] = "6stack-dig",
2941 [AD1988_3STACK] = "3stack",
2942 [AD1988_3STACK_DIG] = "3stack-dig",
2943 [AD1988_LAPTOP] = "laptop",
2944 [AD1988_LAPTOP_DIG] = "laptop-dig",
2945 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002946};
2947
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002948static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002949 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002950 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02002951 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07002952 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002953 {}
2954};
2955
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002956static int patch_ad1988(struct hda_codec *codec)
2957{
2958 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002959 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002960
2961 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2962 if (spec == NULL)
2963 return -ENOMEM;
2964
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002965 codec->spec = spec;
2966
Takashi Iwai1a806f42006-07-03 15:58:16 +02002967 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002968 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
2969
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002970 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002971 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002972 if (board_config < 0) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002973 printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n");
2974 board_config = AD1988_AUTO;
2975 }
2976
2977 if (board_config == AD1988_AUTO) {
2978 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002979 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002980 if (err < 0) {
2981 ad198x_free(codec);
2982 return err;
2983 } else if (! err) {
2984 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
2985 board_config = AD1988_6STACK;
2986 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002987 }
2988
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002989 err = snd_hda_attach_beep_device(codec, 0x10);
2990 if (err < 0) {
2991 ad198x_free(codec);
2992 return err;
2993 }
2994 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
2995
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002996 switch (board_config) {
2997 case AD1988_6STACK:
2998 case AD1988_6STACK_DIG:
2999 spec->multiout.max_channels = 8;
3000 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003001 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003002 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3003 else
3004 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003005 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003006 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003007 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003008 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3009 else
3010 spec->mixers[0] = ad1988_6stack_mixers1;
3011 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003012 spec->num_init_verbs = 1;
3013 spec->init_verbs[0] = ad1988_6stack_init_verbs;
3014 if (board_config == AD1988_6STACK_DIG) {
3015 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3016 spec->dig_in_nid = AD1988_SPDIF_IN;
3017 }
3018 break;
3019 case AD1988_3STACK:
3020 case AD1988_3STACK_DIG:
3021 spec->multiout.max_channels = 6;
3022 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003023 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003024 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3025 else
3026 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003027 spec->input_mux = &ad1988_6stack_capture_source;
3028 spec->channel_mode = ad1988_3stack_modes;
3029 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003030 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003031 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003032 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3033 else
3034 spec->mixers[0] = ad1988_3stack_mixers1;
3035 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003036 spec->num_init_verbs = 1;
3037 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3038 if (board_config == AD1988_3STACK_DIG)
3039 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3040 break;
3041 case AD1988_LAPTOP:
3042 case AD1988_LAPTOP_DIG:
3043 spec->multiout.max_channels = 2;
3044 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003045 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003046 spec->input_mux = &ad1988_laptop_capture_source;
3047 spec->num_mixers = 1;
3048 spec->mixers[0] = ad1988_laptop_mixers;
3049 spec->num_init_verbs = 1;
3050 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3051 if (board_config == AD1988_LAPTOP_DIG)
3052 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3053 break;
3054 }
3055
Takashi Iwaid32410b12005-11-24 16:06:23 +01003056 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3057 spec->adc_nids = ad1988_adc_nids;
3058 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003059 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3060 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3061 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003062 if (codec->vendor_id >= 0x11d4989a) {
3063 spec->mixers[spec->num_mixers++] =
3064 ad1989_spdif_out_mixers;
3065 spec->init_verbs[spec->num_init_verbs++] =
3066 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003067 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003068 } else {
3069 spec->mixers[spec->num_mixers++] =
3070 ad1988_spdif_out_mixers;
3071 spec->init_verbs[spec->num_init_verbs++] =
3072 ad1988_spdif_init_verbs;
3073 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003074 }
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003075 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003076 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
3077
3078 codec->patch_ops = ad198x_patch_ops;
3079 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003080 case AD1988_AUTO:
3081 codec->patch_ops.init = ad1988_auto_init;
3082 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003083 case AD1988_LAPTOP:
3084 case AD1988_LAPTOP_DIG:
3085 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3086 break;
3087 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003088#ifdef CONFIG_SND_HDA_POWER_SAVE
3089 spec->loopback.amplist = ad1988_loopbacks;
3090#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003091 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003092
3093 return 0;
3094}
3095
3096
3097/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003098 * AD1884 / AD1984
3099 *
3100 * port-B - front line/mic-in
3101 * port-E - aux in/out
3102 * port-F - aux in/out
3103 * port-C - rear line/mic-in
3104 * port-D - rear line/hp-out
3105 * port-A - front line/hp-out
3106 *
3107 * AD1984 = AD1884 + two digital mic-ins
3108 *
3109 * FIXME:
3110 * For simplicity, we share the single DAC for both HP and line-outs
3111 * right now. The inidividual playbacks could be easily implemented,
3112 * but no build-up framework is given, so far.
3113 */
3114
3115static hda_nid_t ad1884_dac_nids[1] = {
3116 0x04,
3117};
3118
3119static hda_nid_t ad1884_adc_nids[2] = {
3120 0x08, 0x09,
3121};
3122
3123static hda_nid_t ad1884_capsrc_nids[2] = {
3124 0x0c, 0x0d,
3125};
3126
3127#define AD1884_SPDIF_OUT 0x02
3128
3129static struct hda_input_mux ad1884_capture_source = {
3130 .num_items = 4,
3131 .items = {
3132 { "Front Mic", 0x0 },
3133 { "Mic", 0x1 },
3134 { "CD", 0x2 },
3135 { "Mix", 0x3 },
3136 },
3137};
3138
3139static struct snd_kcontrol_new ad1884_base_mixers[] = {
3140 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3141 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3142 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3143 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3144 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3145 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3146 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3147 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3148 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3149 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3150 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3151 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003152 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
3153 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3154 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3155 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3156 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3157 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3158 {
3159 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3160 /* The multiple "Capture Source" controls confuse alsamixer
3161 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003162 */
3163 /* .name = "Capture Source", */
3164 .name = "Input Source",
3165 .count = 2,
3166 .info = ad198x_mux_enum_info,
3167 .get = ad198x_mux_enum_get,
3168 .put = ad198x_mux_enum_put,
3169 },
3170 /* SPDIF controls */
3171 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3172 {
3173 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3174 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3175 /* identical with ad1983 */
3176 .info = ad1983_spdif_route_info,
3177 .get = ad1983_spdif_route_get,
3178 .put = ad1983_spdif_route_put,
3179 },
3180 { } /* end */
3181};
3182
3183static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3184 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3185 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3186 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003187 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003188 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003189 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003190 { } /* end */
3191};
3192
3193/*
3194 * initialization verbs
3195 */
3196static struct hda_verb ad1884_init_verbs[] = {
3197 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003198 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3199 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003200 /* Port-A (HP) mixer */
3201 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3202 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3203 /* Port-A pin */
3204 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3205 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3206 /* HP selector - select DAC2 */
3207 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3208 /* Port-D (Line-out) mixer */
3209 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3210 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3211 /* Port-D pin */
3212 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3213 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3214 /* Mono-out mixer */
3215 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3216 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3217 /* Mono-out pin */
3218 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3219 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3220 /* Mono selector */
3221 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3222 /* Port-B (front mic) pin */
3223 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003224 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003225 /* Port-C (rear mic) pin */
3226 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003227 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003228 /* Analog mixer; mute as default */
3229 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3230 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3231 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3232 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3233 /* Analog Mix output amp */
3234 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3235 /* SPDIF output selector */
3236 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3237 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3238 { } /* end */
3239};
3240
Takashi Iwaicb53c622007-08-10 17:21:45 +02003241#ifdef CONFIG_SND_HDA_POWER_SAVE
3242static struct hda_amp_list ad1884_loopbacks[] = {
3243 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3244 { 0x20, HDA_INPUT, 1 }, /* Mic */
3245 { 0x20, HDA_INPUT, 2 }, /* CD */
3246 { 0x20, HDA_INPUT, 4 }, /* Docking */
3247 { } /* end */
3248};
3249#endif
3250
Takashi Iwai2134ea42008-01-10 16:53:55 +01003251static const char *ad1884_slave_vols[] = {
3252 "PCM Playback Volume",
3253 "Mic Playback Volume",
3254 "Mono Playback Volume",
3255 "Front Mic Playback Volume",
3256 "Mic Playback Volume",
3257 "CD Playback Volume",
3258 "Internal Mic Playback Volume",
Akinobu Mitabca68462009-04-06 18:42:42 +09003259 "Docking Mic Playback Volume",
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003260 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003261 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003262 NULL
3263};
3264
Takashi Iwai2bac6472007-05-18 18:21:41 +02003265static int patch_ad1884(struct hda_codec *codec)
3266{
3267 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003268 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003269
3270 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3271 if (spec == NULL)
3272 return -ENOMEM;
3273
Takashi Iwai2bac6472007-05-18 18:21:41 +02003274 codec->spec = spec;
3275
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003276 err = snd_hda_attach_beep_device(codec, 0x10);
3277 if (err < 0) {
3278 ad198x_free(codec);
3279 return err;
3280 }
3281 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3282
Takashi Iwai2bac6472007-05-18 18:21:41 +02003283 spec->multiout.max_channels = 2;
3284 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3285 spec->multiout.dac_nids = ad1884_dac_nids;
3286 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3287 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3288 spec->adc_nids = ad1884_adc_nids;
3289 spec->capsrc_nids = ad1884_capsrc_nids;
3290 spec->input_mux = &ad1884_capture_source;
3291 spec->num_mixers = 1;
3292 spec->mixers[0] = ad1884_base_mixers;
3293 spec->num_init_verbs = 1;
3294 spec->init_verbs[0] = ad1884_init_verbs;
3295 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003296#ifdef CONFIG_SND_HDA_POWER_SAVE
3297 spec->loopback.amplist = ad1884_loopbacks;
3298#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003299 spec->vmaster_nid = 0x04;
3300 /* we need to cover all playback volumes */
3301 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003302
3303 codec->patch_ops = ad198x_patch_ops;
3304
3305 return 0;
3306}
3307
3308/*
3309 * Lenovo Thinkpad T61/X61
3310 */
3311static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003312 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003313 .items = {
3314 { "Mic", 0x0 },
3315 { "Internal Mic", 0x1 },
3316 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003317 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003318 },
3319};
3320
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003321
3322/*
3323 * Dell Precision T3400
3324 */
3325static struct hda_input_mux ad1984_dell_desktop_capture_source = {
3326 .num_items = 3,
3327 .items = {
3328 { "Front Mic", 0x0 },
3329 { "Line-In", 0x1 },
3330 { "Mix", 0x3 },
3331 },
3332};
3333
3334
Takashi Iwai2bac6472007-05-18 18:21:41 +02003335static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3336 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3337 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3338 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3339 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3340 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3341 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3342 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3343 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3344 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3345 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003346 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3347 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003348 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003349 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3350 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3351 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3352 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3353 {
3354 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3355 /* The multiple "Capture Source" controls confuse alsamixer
3356 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003357 */
3358 /* .name = "Capture Source", */
3359 .name = "Input Source",
3360 .count = 2,
3361 .info = ad198x_mux_enum_info,
3362 .get = ad198x_mux_enum_get,
3363 .put = ad198x_mux_enum_put,
3364 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003365 /* SPDIF controls */
3366 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3367 {
3368 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3369 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3370 /* identical with ad1983 */
3371 .info = ad1983_spdif_route_info,
3372 .get = ad1983_spdif_route_get,
3373 .put = ad1983_spdif_route_put,
3374 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003375 { } /* end */
3376};
3377
3378/* additional verbs */
3379static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3380 /* Port-E (docking station mic) pin */
3381 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3382 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3383 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003384 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003385 /* Analog mixer - docking mic; mute as default */
3386 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003387 /* enable EAPD bit */
3388 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003389 { } /* end */
3390};
3391
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003392/*
3393 * Dell Precision T3400
3394 */
3395static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
3396 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3397 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3398 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3399 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3400 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3401 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3402 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3403 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3404 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003405 HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
3406 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3407 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3408 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3409 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3410 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3411 {
3412 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3413 /* The multiple "Capture Source" controls confuse alsamixer
3414 * So call somewhat different..
3415 */
3416 /* .name = "Capture Source", */
3417 .name = "Input Source",
3418 .count = 2,
3419 .info = ad198x_mux_enum_info,
3420 .get = ad198x_mux_enum_get,
3421 .put = ad198x_mux_enum_put,
3422 },
3423 { } /* end */
3424};
3425
Takashi Iwai2bac6472007-05-18 18:21:41 +02003426/* Digial MIC ADC NID 0x05 + 0x06 */
3427static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3428 struct hda_codec *codec,
3429 unsigned int stream_tag,
3430 unsigned int format,
3431 struct snd_pcm_substream *substream)
3432{
3433 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3434 stream_tag, 0, format);
3435 return 0;
3436}
3437
3438static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3439 struct hda_codec *codec,
3440 struct snd_pcm_substream *substream)
3441{
Takashi Iwai888afa12008-03-18 09:57:50 +01003442 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003443 return 0;
3444}
3445
3446static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3447 .substreams = 2,
3448 .channels_min = 2,
3449 .channels_max = 2,
3450 .nid = 0x05,
3451 .ops = {
3452 .prepare = ad1984_pcm_dmic_prepare,
3453 .cleanup = ad1984_pcm_dmic_cleanup
3454 },
3455};
3456
3457static int ad1984_build_pcms(struct hda_codec *codec)
3458{
3459 struct ad198x_spec *spec = codec->spec;
3460 struct hda_pcm *info;
3461 int err;
3462
3463 err = ad198x_build_pcms(codec);
3464 if (err < 0)
3465 return err;
3466
3467 info = spec->pcm_rec + codec->num_pcms;
3468 codec->num_pcms++;
3469 info->name = "AD1984 Digital Mic";
3470 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3471 return 0;
3472}
3473
3474/* models */
3475enum {
3476 AD1984_BASIC,
3477 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003478 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003479 AD1984_MODELS
3480};
3481
3482static const char *ad1984_models[AD1984_MODELS] = {
3483 [AD1984_BASIC] = "basic",
3484 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003485 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003486};
3487
3488static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3489 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003490 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003491 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003492 {}
3493};
3494
3495static int patch_ad1984(struct hda_codec *codec)
3496{
3497 struct ad198x_spec *spec;
3498 int board_config, err;
3499
3500 err = patch_ad1884(codec);
3501 if (err < 0)
3502 return err;
3503 spec = codec->spec;
3504 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3505 ad1984_models, ad1984_cfg_tbl);
3506 switch (board_config) {
3507 case AD1984_BASIC:
3508 /* additional digital mics */
3509 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3510 codec->patch_ops.build_pcms = ad1984_build_pcms;
3511 break;
3512 case AD1984_THINKPAD:
Jerone Youngebf00c52008-01-07 12:22:18 +01003513 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003514 spec->input_mux = &ad1984_thinkpad_capture_source;
3515 spec->mixers[0] = ad1984_thinkpad_mixers;
3516 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
3517 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003518 case AD1984_DELL_DESKTOP:
3519 spec->multiout.dig_out_nid = 0;
3520 spec->input_mux = &ad1984_dell_desktop_capture_source;
3521 spec->mixers[0] = ad1984_dell_desktop_mixers;
3522 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003523 }
3524 return 0;
3525}
3526
3527
3528/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003529 * AD1883 / AD1884A / AD1984A / AD1984B
3530 *
3531 * port-B (0x14) - front mic-in
3532 * port-E (0x1c) - rear mic-in
3533 * port-F (0x16) - CD / ext out
3534 * port-C (0x15) - rear line-in
3535 * port-D (0x12) - rear line-out
3536 * port-A (0x11) - front hp-out
3537 *
3538 * AD1984A = AD1884A + digital-mic
3539 * AD1883 = equivalent with AD1984A
3540 * AD1984B = AD1984A + extra SPDIF-out
3541 *
3542 * FIXME:
3543 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3544 */
3545
3546static hda_nid_t ad1884a_dac_nids[1] = {
3547 0x03,
3548};
3549
3550#define ad1884a_adc_nids ad1884_adc_nids
3551#define ad1884a_capsrc_nids ad1884_capsrc_nids
3552
3553#define AD1884A_SPDIF_OUT 0x02
3554
3555static struct hda_input_mux ad1884a_capture_source = {
3556 .num_items = 5,
3557 .items = {
3558 { "Front Mic", 0x0 },
3559 { "Mic", 0x4 },
3560 { "Line", 0x1 },
3561 { "CD", 0x2 },
3562 { "Mix", 0x3 },
3563 },
3564};
3565
3566static struct snd_kcontrol_new ad1884a_base_mixers[] = {
3567 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3568 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3569 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3570 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3571 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3572 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3573 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3574 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3575 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3576 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3577 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3578 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3579 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3580 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3581 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3582 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003583 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3584 HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
3585 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3586 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3587 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3588 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3589 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3590 {
3591 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3592 /* The multiple "Capture Source" controls confuse alsamixer
3593 * So call somewhat different..
3594 */
3595 /* .name = "Capture Source", */
3596 .name = "Input Source",
3597 .count = 2,
3598 .info = ad198x_mux_enum_info,
3599 .get = ad198x_mux_enum_get,
3600 .put = ad198x_mux_enum_put,
3601 },
3602 /* SPDIF controls */
3603 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3604 {
3605 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3606 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3607 /* identical with ad1983 */
3608 .info = ad1983_spdif_route_info,
3609 .get = ad1983_spdif_route_get,
3610 .put = ad1983_spdif_route_put,
3611 },
3612 { } /* end */
3613};
3614
3615/*
3616 * initialization verbs
3617 */
3618static struct hda_verb ad1884a_init_verbs[] = {
3619 /* DACs; unmute as default */
3620 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3621 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3622 /* Port-A (HP) mixer - route only from analog mixer */
3623 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3624 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3625 /* Port-A pin */
3626 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3627 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3628 /* Port-D (Line-out) mixer - route only from analog mixer */
3629 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3630 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3631 /* Port-D pin */
3632 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3633 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3634 /* Mono-out mixer - route only from analog mixer */
3635 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3636 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3637 /* Mono-out pin */
3638 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3639 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3640 /* Port-B (front mic) pin */
3641 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003642 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003643 /* Port-C (rear line-in) pin */
3644 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003645 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003646 /* Port-E (rear mic) pin */
3647 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3648 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3649 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
3650 /* Port-F (CD) pin */
3651 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3652 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3653 /* Analog mixer; mute as default */
3654 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3655 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3656 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3657 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3658 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
3659 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3660 /* Analog Mix output amp */
3661 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3662 /* capture sources */
3663 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
3664 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3665 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3666 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3667 /* SPDIF output amp */
3668 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3669 { } /* end */
3670};
3671
3672#ifdef CONFIG_SND_HDA_POWER_SAVE
3673static struct hda_amp_list ad1884a_loopbacks[] = {
3674 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3675 { 0x20, HDA_INPUT, 1 }, /* Mic */
3676 { 0x20, HDA_INPUT, 2 }, /* CD */
3677 { 0x20, HDA_INPUT, 4 }, /* Docking */
3678 { } /* end */
3679};
3680#endif
3681
3682/*
3683 * Laptop model
3684 *
3685 * Port A: Headphone jack
3686 * Port B: MIC jack
3687 * Port C: Internal MIC
3688 * Port D: Dock Line Out (if enabled)
3689 * Port E: Dock Line In (if enabled)
3690 * Port F: Internal speakers
3691 */
3692
3693static struct hda_input_mux ad1884a_laptop_capture_source = {
3694 .num_items = 4,
3695 .items = {
3696 { "Mic", 0x0 }, /* port-B */
3697 { "Internal Mic", 0x1 }, /* port-C */
3698 { "Dock Mic", 0x4 }, /* port-E */
3699 { "Mix", 0x3 },
3700 },
3701};
3702
3703static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
3704 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3705 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3706 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3707 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3708 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3709 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3710 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3711 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3712 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3713 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3714 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003715 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3716 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
3717 HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3718 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3719 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3720 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3721 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3722 {
3723 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3724 /* The multiple "Capture Source" controls confuse alsamixer
3725 * So call somewhat different..
3726 */
3727 /* .name = "Capture Source", */
3728 .name = "Input Source",
3729 .count = 2,
3730 .info = ad198x_mux_enum_info,
3731 .get = ad198x_mux_enum_get,
3732 .put = ad198x_mux_enum_put,
3733 },
3734 { } /* end */
3735};
3736
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003737static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
3738 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3739 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3740 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3741 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02003742 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
3743 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003744 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3745 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003746 { } /* end */
3747};
3748
Takashi Iwaic5059252008-02-16 09:43:56 +01003749/* mute internal speaker if HP is plugged */
3750static void ad1884a_hp_automute(struct hda_codec *codec)
3751{
3752 unsigned int present;
3753
3754 present = snd_hda_codec_read(codec, 0x11, 0,
3755 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
3756 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3757 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3758 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3759 present ? 0x00 : 0x02);
3760}
3761
Takashi Iwai269ef192008-05-30 15:32:15 +02003762/* switch to external mic if plugged */
3763static void ad1884a_hp_automic(struct hda_codec *codec)
3764{
3765 unsigned int present;
3766
3767 present = snd_hda_codec_read(codec, 0x14, 0,
3768 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
3769 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
3770 present ? 0 : 1);
3771}
3772
Takashi Iwaic5059252008-02-16 09:43:56 +01003773#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02003774#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01003775
3776/* unsolicited event for HP jack sensing */
3777static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
3778{
Takashi Iwai269ef192008-05-30 15:32:15 +02003779 switch (res >> 26) {
3780 case AD1884A_HP_EVENT:
3781 ad1884a_hp_automute(codec);
3782 break;
3783 case AD1884A_MIC_EVENT:
3784 ad1884a_hp_automic(codec);
3785 break;
3786 }
Takashi Iwaic5059252008-02-16 09:43:56 +01003787}
3788
3789/* initialize jack-sensing, too */
3790static int ad1884a_hp_init(struct hda_codec *codec)
3791{
3792 ad198x_init(codec);
3793 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02003794 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01003795 return 0;
3796}
3797
3798/* additional verbs for laptop model */
3799static struct hda_verb ad1884a_laptop_verbs[] = {
3800 /* Port-A (HP) pin - always unmuted */
3801 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3802 /* Port-F (int speaker) mixer - route only from analog mixer */
3803 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3804 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3805 /* Port-F pin */
3806 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3807 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Takashi Iwai269ef192008-05-30 15:32:15 +02003808 /* Port-C pin - internal mic-in */
3809 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3810 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3811 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwaic5059252008-02-16 09:43:56 +01003812 /* analog mix */
3813 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3814 /* unsolicited event for pin-sense */
3815 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003816 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaic5059252008-02-16 09:43:56 +01003817 { } /* end */
3818};
3819
3820/*
Takashi Iwaif0813742008-03-18 12:13:03 +01003821 * Thinkpad X300
3822 * 0x11 - HP
3823 * 0x12 - speaker
3824 * 0x14 - mic-in
3825 * 0x17 - built-in mic
3826 */
3827
3828static struct hda_verb ad1984a_thinkpad_verbs[] = {
3829 /* HP unmute */
3830 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3831 /* analog mix */
3832 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3833 /* turn on EAPD */
3834 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
3835 /* unsolicited event for pin-sense */
3836 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
3837 /* internal mic - dmic */
3838 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02003839 /* set magic COEFs for dmic */
3840 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
3841 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01003842 { } /* end */
3843};
3844
3845static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
3846 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3847 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3848 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3849 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3850 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3851 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01003852 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3853 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
3854 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3855 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3856 {
3857 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3858 .name = "Capture Source",
3859 .info = ad198x_mux_enum_info,
3860 .get = ad198x_mux_enum_get,
3861 .put = ad198x_mux_enum_put,
3862 },
3863 { } /* end */
3864};
3865
3866static struct hda_input_mux ad1984a_thinkpad_capture_source = {
3867 .num_items = 3,
3868 .items = {
3869 { "Mic", 0x0 },
3870 { "Internal Mic", 0x5 },
3871 { "Mix", 0x3 },
3872 },
3873};
3874
3875/* mute internal speaker if HP is plugged */
3876static void ad1984a_thinkpad_automute(struct hda_codec *codec)
3877{
3878 unsigned int present;
3879
3880 present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0)
3881 & AC_PINSENSE_PRESENCE;
3882 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
3883 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3884}
3885
3886/* unsolicited event for HP jack sensing */
3887static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
3888 unsigned int res)
3889{
3890 if ((res >> 26) != AD1884A_HP_EVENT)
3891 return;
3892 ad1984a_thinkpad_automute(codec);
3893}
3894
3895/* initialize jack-sensing, too */
3896static int ad1984a_thinkpad_init(struct hda_codec *codec)
3897{
3898 ad198x_init(codec);
3899 ad1984a_thinkpad_automute(codec);
3900 return 0;
3901}
3902
3903/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003904 */
3905
3906enum {
3907 AD1884A_DESKTOP,
3908 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003909 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01003910 AD1884A_THINKPAD,
Takashi Iwaic5059252008-02-16 09:43:56 +01003911 AD1884A_MODELS
3912};
3913
3914static const char *ad1884a_models[AD1884A_MODELS] = {
3915 [AD1884A_DESKTOP] = "desktop",
3916 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003917 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01003918 [AD1884A_THINKPAD] = "thinkpad",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003919};
3920
3921static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
3922 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01003923 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01003924 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01003925 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwai873dc782009-02-25 18:12:13 +01003926 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
3927 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwaif0813742008-03-18 12:13:03 +01003928 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003929 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01003930};
3931
3932static int patch_ad1884a(struct hda_codec *codec)
3933{
3934 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003935 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01003936
3937 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3938 if (spec == NULL)
3939 return -ENOMEM;
3940
Takashi Iwaic5059252008-02-16 09:43:56 +01003941 codec->spec = spec;
3942
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003943 err = snd_hda_attach_beep_device(codec, 0x10);
3944 if (err < 0) {
3945 ad198x_free(codec);
3946 return err;
3947 }
3948 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3949
Takashi Iwaic5059252008-02-16 09:43:56 +01003950 spec->multiout.max_channels = 2;
3951 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
3952 spec->multiout.dac_nids = ad1884a_dac_nids;
3953 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
3954 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
3955 spec->adc_nids = ad1884a_adc_nids;
3956 spec->capsrc_nids = ad1884a_capsrc_nids;
3957 spec->input_mux = &ad1884a_capture_source;
3958 spec->num_mixers = 1;
3959 spec->mixers[0] = ad1884a_base_mixers;
3960 spec->num_init_verbs = 1;
3961 spec->init_verbs[0] = ad1884a_init_verbs;
3962 spec->spdif_route = 0;
3963#ifdef CONFIG_SND_HDA_POWER_SAVE
3964 spec->loopback.amplist = ad1884a_loopbacks;
3965#endif
3966 codec->patch_ops = ad198x_patch_ops;
3967
3968 /* override some parameters */
3969 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003970 ad1884a_models,
3971 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01003972 switch (board_config) {
3973 case AD1884A_LAPTOP:
3974 spec->mixers[0] = ad1884a_laptop_mixers;
3975 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
3976 spec->multiout.dig_out_nid = 0;
3977 spec->input_mux = &ad1884a_laptop_capture_source;
3978 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
3979 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02003980 /* set the upper-limit for mixer amp to 0dB for avoiding the
3981 * possible damage by overloading
3982 */
3983 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
3984 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3985 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3986 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3987 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01003988 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003989 case AD1884A_MOBILE:
3990 spec->mixers[0] = ad1884a_mobile_mixers;
3991 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
3992 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003993 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
3994 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01003995 /* set the upper-limit for mixer amp to 0dB for avoiding the
3996 * possible damage by overloading
3997 */
3998 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
3999 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4000 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4001 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4002 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004003 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004004 case AD1884A_THINKPAD:
4005 spec->mixers[0] = ad1984a_thinkpad_mixers;
4006 spec->init_verbs[spec->num_init_verbs++] =
4007 ad1984a_thinkpad_verbs;
4008 spec->multiout.dig_out_nid = 0;
4009 spec->input_mux = &ad1984a_thinkpad_capture_source;
4010 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4011 codec->patch_ops.init = ad1984a_thinkpad_init;
4012 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004013 }
4014
4015 return 0;
4016}
4017
4018
4019/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004020 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004021 *
4022 * port-A - front hp-out
4023 * port-B - front mic-in
4024 * port-C - rear line-in, shared surr-out (3stack)
4025 * port-D - rear line-out
4026 * port-E - rear mic-in, shared clfe-out (3stack)
4027 * port-F - rear surr-out (6stack)
4028 * port-G - rear clfe-out (6stack)
4029 */
4030
4031static hda_nid_t ad1882_dac_nids[3] = {
4032 0x04, 0x03, 0x05
4033};
4034
4035static hda_nid_t ad1882_adc_nids[2] = {
4036 0x08, 0x09,
4037};
4038
4039static hda_nid_t ad1882_capsrc_nids[2] = {
4040 0x0c, 0x0d,
4041};
4042
4043#define AD1882_SPDIF_OUT 0x02
4044
4045/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
4046static struct hda_input_mux ad1882_capture_source = {
4047 .num_items = 5,
4048 .items = {
4049 { "Front Mic", 0x1 },
4050 { "Mic", 0x4 },
4051 { "Line", 0x2 },
4052 { "CD", 0x3 },
4053 { "Mix", 0x7 },
4054 },
4055};
4056
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004057/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
4058static struct hda_input_mux ad1882a_capture_source = {
4059 .num_items = 5,
4060 .items = {
4061 { "Front Mic", 0x1 },
4062 { "Mic", 0x4},
4063 { "Line", 0x2 },
4064 { "Digital Mic", 0x06 },
4065 { "Mix", 0x7 },
4066 },
4067};
4068
Takashi Iwai0ac85512007-06-20 15:46:13 +02004069static struct snd_kcontrol_new ad1882_base_mixers[] = {
4070 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4071 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4072 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4073 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4074 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4075 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4076 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4077 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004078
Takashi Iwai0ac85512007-06-20 15:46:13 +02004079 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
4080 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
4081 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
4082 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4083 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4084 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4085 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4086 {
4087 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4088 /* The multiple "Capture Source" controls confuse alsamixer
4089 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004090 */
4091 /* .name = "Capture Source", */
4092 .name = "Input Source",
4093 .count = 2,
4094 .info = ad198x_mux_enum_info,
4095 .get = ad198x_mux_enum_get,
4096 .put = ad198x_mux_enum_put,
4097 },
4098 /* SPDIF controls */
4099 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4100 {
4101 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4102 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4103 /* identical with ad1983 */
4104 .info = ad1983_spdif_route_info,
4105 .get = ad1983_spdif_route_get,
4106 .put = ad1983_spdif_route_put,
4107 },
4108 { } /* end */
4109};
4110
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004111static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
4112 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4113 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4114 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4115 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4116 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4117 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4118 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4119 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004120 { } /* end */
4121};
4122
4123static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
4124 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4125 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4126 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4127 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4128 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4129 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4130 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4131 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004132 HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
4133 { } /* end */
4134};
4135
Takashi Iwai0ac85512007-06-20 15:46:13 +02004136static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
4137 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4138 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4139 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4140 {
4141 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4142 .name = "Channel Mode",
4143 .info = ad198x_ch_mode_info,
4144 .get = ad198x_ch_mode_get,
4145 .put = ad198x_ch_mode_put,
4146 },
4147 { } /* end */
4148};
4149
4150static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
4151 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4152 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4153 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4154 { } /* end */
4155};
4156
4157static struct hda_verb ad1882_ch2_init[] = {
4158 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4159 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4160 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4161 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4162 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4163 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4164 { } /* end */
4165};
4166
4167static struct hda_verb ad1882_ch4_init[] = {
4168 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4169 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4170 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4171 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4172 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4173 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4174 { } /* end */
4175};
4176
4177static struct hda_verb ad1882_ch6_init[] = {
4178 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4179 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4180 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4181 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4182 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4183 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4184 { } /* end */
4185};
4186
4187static struct hda_channel_mode ad1882_modes[3] = {
4188 { 2, ad1882_ch2_init },
4189 { 4, ad1882_ch4_init },
4190 { 6, ad1882_ch6_init },
4191};
4192
4193/*
4194 * initialization verbs
4195 */
4196static struct hda_verb ad1882_init_verbs[] = {
4197 /* DACs; mute as default */
4198 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4199 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4200 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4201 /* Port-A (HP) mixer */
4202 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4203 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4204 /* Port-A pin */
4205 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4206 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4207 /* HP selector - select DAC2 */
4208 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4209 /* Port-D (Line-out) mixer */
4210 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4211 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4212 /* Port-D pin */
4213 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4214 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4215 /* Mono-out mixer */
4216 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4217 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4218 /* Mono-out pin */
4219 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4220 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4221 /* Port-B (front mic) pin */
4222 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4223 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4224 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4225 /* Port-C (line-in) pin */
4226 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4227 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4228 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4229 /* Port-C mixer - mute as input */
4230 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4231 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4232 /* Port-E (mic-in) pin */
4233 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4234 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4235 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4236 /* Port-E mixer - mute as input */
4237 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4238 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4239 /* Port-F (surround) */
4240 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4241 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4242 /* Port-G (CLFE) */
4243 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4244 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4245 /* Analog mixer; mute as default */
4246 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4247 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4248 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4249 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4250 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4251 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4252 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4253 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4254 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4255 /* Analog Mix output amp */
4256 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4257 /* SPDIF output selector */
4258 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4259 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4260 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4261 { } /* end */
4262};
4263
Takashi Iwaicb53c622007-08-10 17:21:45 +02004264#ifdef CONFIG_SND_HDA_POWER_SAVE
4265static struct hda_amp_list ad1882_loopbacks[] = {
4266 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4267 { 0x20, HDA_INPUT, 1 }, /* Mic */
4268 { 0x20, HDA_INPUT, 4 }, /* Line */
4269 { 0x20, HDA_INPUT, 6 }, /* CD */
4270 { } /* end */
4271};
4272#endif
4273
Takashi Iwai0ac85512007-06-20 15:46:13 +02004274/* models */
4275enum {
4276 AD1882_3STACK,
4277 AD1882_6STACK,
4278 AD1882_MODELS
4279};
4280
4281static const char *ad1882_models[AD1986A_MODELS] = {
4282 [AD1882_3STACK] = "3stack",
4283 [AD1882_6STACK] = "6stack",
4284};
4285
4286
4287static int patch_ad1882(struct hda_codec *codec)
4288{
4289 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004290 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004291
4292 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4293 if (spec == NULL)
4294 return -ENOMEM;
4295
Takashi Iwai0ac85512007-06-20 15:46:13 +02004296 codec->spec = spec;
4297
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004298 err = snd_hda_attach_beep_device(codec, 0x10);
4299 if (err < 0) {
4300 ad198x_free(codec);
4301 return err;
4302 }
4303 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4304
Takashi Iwai0ac85512007-06-20 15:46:13 +02004305 spec->multiout.max_channels = 6;
4306 spec->multiout.num_dacs = 3;
4307 spec->multiout.dac_nids = ad1882_dac_nids;
4308 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
4309 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
4310 spec->adc_nids = ad1882_adc_nids;
4311 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004312 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004313 spec->input_mux = &ad1882_capture_source;
4314 else
4315 spec->input_mux = &ad1882a_capture_source;
4316 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004317 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004318 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004319 spec->mixers[1] = ad1882_loopback_mixers;
4320 else
4321 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004322 spec->num_init_verbs = 1;
4323 spec->init_verbs[0] = ad1882_init_verbs;
4324 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02004325#ifdef CONFIG_SND_HDA_POWER_SAVE
4326 spec->loopback.amplist = ad1882_loopbacks;
4327#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01004328 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004329
4330 codec->patch_ops = ad198x_patch_ops;
4331
4332 /* override some parameters */
4333 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
4334 ad1882_models, NULL);
4335 switch (board_config) {
4336 default:
4337 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004338 spec->num_mixers = 3;
4339 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004340 spec->channel_mode = ad1882_modes;
4341 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
4342 spec->need_dac_fix = 1;
4343 spec->multiout.max_channels = 2;
4344 spec->multiout.num_dacs = 1;
4345 break;
4346 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004347 spec->num_mixers = 3;
4348 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004349 break;
4350 }
4351 return 0;
4352}
4353
4354
4355/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004356 * patch entries
4357 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004358static struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004359 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02004360 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004361 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004362 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004363 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
4364 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02004365 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
4366 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004367 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004368 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01004369 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02004370 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004371 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02004372 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
4373 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004374 {} /* terminator */
4375};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004376
4377MODULE_ALIAS("snd-hda-codec-id:11d4*");
4378
4379MODULE_LICENSE("GPL");
4380MODULE_DESCRIPTION("Analog Devices HD-audio codec");
4381
4382static struct hda_codec_preset_list analog_list = {
4383 .preset = snd_hda_preset_analog,
4384 .owner = THIS_MODULE,
4385};
4386
4387static int __init patch_analog_init(void)
4388{
4389 return snd_hda_add_codec_preset(&analog_list);
4390}
4391
4392static void __exit patch_analog_exit(void)
4393{
4394 snd_hda_delete_codec_preset(&analog_list);
4395}
4396
4397module_init(patch_analog_init)
4398module_exit(patch_analog_exit)