blob: 45ee352df3298d65abc34191b26bb95a23718d59 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Takashi Iwai0ac85512007-06-20 15:46:13 +02002 * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
3 * AD1986A, AD1988
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
Takashi Iwai2bac6472007-05-18 18:21:41 +02005 * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 *
7 * This driver is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This driver is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/init.h>
23#include <linux/delay.h>
24#include <linux/slab.h>
25#include <linux/pci.h>
Ingo Molnar62932df2006-01-16 16:34:20 +010026
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <sound/core.h>
28#include "hda_codec.h"
29#include "hda_local.h"
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010030#include "hda_beep.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020032struct ad198x_spec {
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010033 struct snd_kcontrol_new *mixers[5];
Takashi Iwai985be542005-11-02 18:26:49 +010034 int num_mixers;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010035 unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
Takashi Iwaid32410b12005-11-24 16:06:23 +010036 const struct hda_verb *init_verbs[5]; /* initialization verbs
Takashi Iwai985be542005-11-02 18:26:49 +010037 * don't forget NULL termination!
38 */
39 unsigned int num_init_verbs;
40
41 /* playback */
42 struct hda_multi_out multiout; /* playback set-up
43 * max_channels, dacs must be set
44 * dig_out_nid and hp_nid are optional
45 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +010046 unsigned int cur_eapd;
Takashi Iwai2125cad2006-03-27 12:52:22 +020047 unsigned int need_dac_fix;
Takashi Iwai985be542005-11-02 18:26:49 +010048
49 /* capture */
50 unsigned int num_adc_nids;
51 hda_nid_t *adc_nids;
52 hda_nid_t dig_in_nid; /* digital-in NID; optional */
53
54 /* capture source */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020055 const struct hda_input_mux *input_mux;
Takashi Iwai2e5b9562005-11-21 16:36:15 +010056 hda_nid_t *capsrc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010057 unsigned int cur_mux[3];
58
59 /* channel model */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +010060 const struct hda_channel_mode *channel_mode;
Takashi Iwai985be542005-11-02 18:26:49 +010061 int num_channel_mode;
62
63 /* PCM information */
Takashi Iwai2bac6472007-05-18 18:21:41 +020064 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
Takashi Iwai985be542005-11-02 18:26:49 +010065
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020066 unsigned int spdif_route;
Takashi Iwaid32410b12005-11-24 16:06:23 +010067
68 /* dynamic controls, init_verbs and input_mux */
69 struct auto_pin_cfg autocfg;
Takashi Iwai603c4012008-07-30 15:01:44 +020070 struct snd_array kctls;
Takashi Iwaid32410b12005-11-24 16:06:23 +010071 struct hda_input_mux private_imux;
Takashi Iwai41923e42007-10-22 17:20:10 +020072 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
Takashi Iwaicb53c622007-08-10 17:21:45 +020073
Takashi Iwai8ab78c72007-09-06 14:29:53 +020074 unsigned int jack_present :1;
Takashi Iwaiee6e3652009-12-08 17:23:33 +010075 unsigned int inv_jack_detect:1; /* inverted jack-detection */
76 unsigned int inv_eapd:1; /* inverted EAPD implementation */
Takashi Iwai8ab78c72007-09-06 14:29:53 +020077
Takashi Iwaicb53c622007-08-10 17:21:45 +020078#ifdef CONFIG_SND_HDA_POWER_SAVE
79 struct hda_loopback_check loopback;
80#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +010081 /* for virtual master */
82 hda_nid_t vmaster_nid;
Takashi Iwai2134ea42008-01-10 16:53:55 +010083 const char **slave_vols;
84 const char **slave_sws;
Linus Torvalds1da177e2005-04-16 15:20:36 -070085};
86
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020087/*
88 * input MUX handling (common part)
89 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010090static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020091{
92 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
93 struct ad198x_spec *spec = codec->spec;
94
95 return snd_hda_input_mux_info(spec->input_mux, uinfo);
96}
97
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010098static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020099{
100 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
101 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100102 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200103
Takashi Iwai985be542005-11-02 18:26:49 +0100104 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200105 return 0;
106}
107
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100108static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200109{
110 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
111 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100112 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200113
114 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai2e5b9562005-11-21 16:36:15 +0100115 spec->capsrc_nids[adc_idx],
116 &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200117}
118
119/*
120 * initialization (common callbacks)
121 */
122static int ad198x_init(struct hda_codec *codec)
123{
124 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100125 int i;
126
127 for (i = 0; i < spec->num_init_verbs; i++)
128 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200129 return 0;
130}
131
Takashi Iwai2134ea42008-01-10 16:53:55 +0100132static const char *ad_slave_vols[] = {
133 "Front Playback Volume",
134 "Surround Playback Volume",
135 "Center Playback Volume",
136 "LFE Playback Volume",
137 "Side Playback Volume",
138 "Headphone Playback Volume",
139 "Mono Playback Volume",
Takashi Iwai628ed132008-01-25 11:56:57 +0100140 "Speaker Playback Volume",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100141 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100142 NULL
143};
144
145static const char *ad_slave_sws[] = {
146 "Front Playback Switch",
147 "Surround Playback Switch",
148 "Center Playback Switch",
149 "LFE Playback Switch",
150 "Side Playback Switch",
151 "Headphone Playback Switch",
152 "Mono Playback Switch",
Takashi Iwai628ed132008-01-25 11:56:57 +0100153 "Speaker Playback Switch",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100154 "IEC958 Playback Switch",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100155 NULL
156};
157
Takashi Iwai603c4012008-07-30 15:01:44 +0200158static void ad198x_free_kctls(struct hda_codec *codec);
159
Takashi Iwai67d634c2009-11-16 15:35:59 +0100160#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100161/* additional beep mixers; the actual parameters are overwritten at build */
162static struct snd_kcontrol_new ad_beep_mixer[] = {
163 HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
Jaroslav Kysela123c07a2009-10-21 14:48:23 +0200164 HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100165 { } /* end */
166};
167
168#define set_beep_amp(spec, nid, idx, dir) \
169 ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100170#else
171#define set_beep_amp(spec, nid, idx, dir) /* NOP */
172#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100173
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200174static int ad198x_build_controls(struct hda_codec *codec)
175{
176 struct ad198x_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100177 struct snd_kcontrol *kctl;
Takashi Iwai985be542005-11-02 18:26:49 +0100178 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200179 int err;
180
Takashi Iwai985be542005-11-02 18:26:49 +0100181 for (i = 0; i < spec->num_mixers; i++) {
182 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
183 if (err < 0)
184 return err;
185 }
186 if (spec->multiout.dig_out_nid) {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200187 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100188 if (err < 0)
189 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100190 err = snd_hda_create_spdif_share_sw(codec,
191 &spec->multiout);
192 if (err < 0)
193 return err;
194 spec->multiout.share_spdif = 1;
Takashi Iwai985be542005-11-02 18:26:49 +0100195 }
196 if (spec->dig_in_nid) {
197 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
198 if (err < 0)
199 return err;
200 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100201
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100202 /* create beep controls if needed */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100203#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100204 if (spec->beep_amp) {
205 struct snd_kcontrol_new *knew;
206 for (knew = ad_beep_mixer; knew->name; knew++) {
207 struct snd_kcontrol *kctl;
208 kctl = snd_ctl_new1(knew, codec);
209 if (!kctl)
210 return -ENOMEM;
211 kctl->private_value = spec->beep_amp;
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100212 err = snd_hda_ctl_add(codec, 0, kctl);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100213 if (err < 0)
214 return err;
215 }
216 }
Takashi Iwai67d634c2009-11-16 15:35:59 +0100217#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100218
Takashi Iwai2134ea42008-01-10 16:53:55 +0100219 /* if we have no master control, let's create it */
220 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100221 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100222 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100223 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100224 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100225 vmaster_tlv,
Takashi Iwai2134ea42008-01-10 16:53:55 +0100226 (spec->slave_vols ?
227 spec->slave_vols : ad_slave_vols));
228 if (err < 0)
229 return err;
230 }
231 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
232 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
233 NULL,
234 (spec->slave_sws ?
235 spec->slave_sws : ad_slave_sws));
236 if (err < 0)
237 return err;
238 }
239
Takashi Iwai603c4012008-07-30 15:01:44 +0200240 ad198x_free_kctls(codec); /* no longer needed */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100241
242 /* assign Capture Source enums to NID */
243 kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
244 if (!kctl)
245 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
246 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +0100247 err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100248 if (err < 0)
249 return err;
250 }
251
252 /* assign IEC958 enums to NID */
253 kctl = snd_hda_find_mixer_ctl(codec,
254 SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
255 if (kctl) {
256 err = snd_hda_add_nid(codec, kctl, 0,
257 spec->multiout.dig_out_nid);
258 if (err < 0)
259 return err;
260 }
261
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200262 return 0;
263}
264
Takashi Iwaicb53c622007-08-10 17:21:45 +0200265#ifdef CONFIG_SND_HDA_POWER_SAVE
266static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
267{
268 struct ad198x_spec *spec = codec->spec;
269 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
270}
271#endif
272
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200273/*
274 * Analog playback callbacks
275 */
276static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
277 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100278 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200279{
280 struct ad198x_spec *spec = codec->spec;
Takashi Iwai9a081602008-02-12 18:37:26 +0100281 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
282 hinfo);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200283}
284
285static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
286 struct hda_codec *codec,
287 unsigned int stream_tag,
288 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100289 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200290{
291 struct ad198x_spec *spec = codec->spec;
292 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
293 format, substream);
294}
295
296static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
297 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100298 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200299{
300 struct ad198x_spec *spec = codec->spec;
301 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
302}
303
304/*
305 * Digital out
306 */
307static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
308 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100309 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200310{
311 struct ad198x_spec *spec = codec->spec;
312 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
313}
314
315static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
316 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100317 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200318{
319 struct ad198x_spec *spec = codec->spec;
320 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
321}
322
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200323static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
324 struct hda_codec *codec,
325 unsigned int stream_tag,
326 unsigned int format,
327 struct snd_pcm_substream *substream)
328{
329 struct ad198x_spec *spec = codec->spec;
330 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
331 format, substream);
332}
333
Takashi Iwai9411e212009-02-13 11:32:28 +0100334static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
335 struct hda_codec *codec,
336 struct snd_pcm_substream *substream)
337{
338 struct ad198x_spec *spec = codec->spec;
339 return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
340}
341
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200342/*
343 * Analog capture
344 */
345static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
346 struct hda_codec *codec,
347 unsigned int stream_tag,
348 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100349 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200350{
351 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100352 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
353 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200354 return 0;
355}
356
357static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
358 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100359 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200360{
361 struct ad198x_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +0100362 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200363 return 0;
364}
365
366
367/*
368 */
369static struct hda_pcm_stream ad198x_pcm_analog_playback = {
370 .substreams = 1,
371 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100372 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200373 .nid = 0, /* fill later */
374 .ops = {
375 .open = ad198x_playback_pcm_open,
376 .prepare = ad198x_playback_pcm_prepare,
377 .cleanup = ad198x_playback_pcm_cleanup
378 },
379};
380
381static struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100382 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200383 .channels_min = 2,
384 .channels_max = 2,
385 .nid = 0, /* fill later */
386 .ops = {
387 .prepare = ad198x_capture_pcm_prepare,
388 .cleanup = ad198x_capture_pcm_cleanup
389 },
390};
391
392static struct hda_pcm_stream ad198x_pcm_digital_playback = {
393 .substreams = 1,
394 .channels_min = 2,
395 .channels_max = 2,
396 .nid = 0, /* fill later */
397 .ops = {
398 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200399 .close = ad198x_dig_playback_pcm_close,
Takashi Iwai9411e212009-02-13 11:32:28 +0100400 .prepare = ad198x_dig_playback_pcm_prepare,
401 .cleanup = ad198x_dig_playback_pcm_cleanup
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200402 },
403};
404
Takashi Iwai985be542005-11-02 18:26:49 +0100405static struct hda_pcm_stream ad198x_pcm_digital_capture = {
406 .substreams = 1,
407 .channels_min = 2,
408 .channels_max = 2,
409 /* NID is set in alc_build_pcms */
410};
411
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200412static int ad198x_build_pcms(struct hda_codec *codec)
413{
414 struct ad198x_spec *spec = codec->spec;
415 struct hda_pcm *info = spec->pcm_rec;
416
417 codec->num_pcms = 1;
418 codec->pcm_info = info;
419
420 info->name = "AD198x Analog";
421 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
422 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
423 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
424 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100425 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
426 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200427
428 if (spec->multiout.dig_out_nid) {
429 info++;
430 codec->num_pcms++;
431 info->name = "AD198x Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +0100432 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200433 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
434 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100435 if (spec->dig_in_nid) {
436 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
437 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
438 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200439 }
440
441 return 0;
442}
443
Takashi Iwai603c4012008-07-30 15:01:44 +0200444static void ad198x_free_kctls(struct hda_codec *codec)
445{
446 struct ad198x_spec *spec = codec->spec;
447
448 if (spec->kctls.list) {
449 struct snd_kcontrol_new *kctl = spec->kctls.list;
450 int i;
451 for (i = 0; i < spec->kctls.used; i++)
452 kfree(kctl[i].name);
453 }
454 snd_array_free(&spec->kctls);
455}
456
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200457static void ad198x_free(struct hda_codec *codec)
458{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100459 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +0100460
Takashi Iwai603c4012008-07-30 15:01:44 +0200461 if (!spec)
462 return;
463
464 ad198x_free_kctls(codec);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100465 kfree(spec);
466 snd_hda_detach_beep_device(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200467}
468
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200469static struct hda_codec_ops ad198x_patch_ops = {
470 .build_controls = ad198x_build_controls,
471 .build_pcms = ad198x_build_pcms,
472 .init = ad198x_init,
473 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200474#ifdef CONFIG_SND_HDA_POWER_SAVE
475 .check_power_status = ad198x_check_power_status,
476#endif
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200477};
478
479
480/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100481 * EAPD control
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100482 * the private value = nid
Takashi Iwai18a815d2006-03-01 19:54:39 +0100483 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200484#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100485
486static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
487 struct snd_ctl_elem_value *ucontrol)
488{
489 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
490 struct ad198x_spec *spec = codec->spec;
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100491 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100492 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
493 else
494 ucontrol->value.integer.value[0] = spec->cur_eapd;
495 return 0;
496}
497
498static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
499 struct snd_ctl_elem_value *ucontrol)
500{
501 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
502 struct ad198x_spec *spec = codec->spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +0100503 hda_nid_t nid = kcontrol->private_value & 0xff;
504 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100505 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100506 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100507 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200508 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100509 return 0;
510 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200511 snd_hda_codec_write_cache(codec, nid,
512 0, AC_VERB_SET_EAPD_BTLENABLE,
513 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100514 return 1;
515}
516
Takashi Iwai9230d212006-03-13 13:49:49 +0100517static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
518 struct snd_ctl_elem_info *uinfo);
519static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
520 struct snd_ctl_elem_value *ucontrol);
521static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
522 struct snd_ctl_elem_value *ucontrol);
523
524
Takashi Iwai18a815d2006-03-01 19:54:39 +0100525/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200526 * AD1986A specific
527 */
528
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529#define AD1986A_SPDIF_OUT 0x02
530#define AD1986A_FRONT_DAC 0x03
531#define AD1986A_SURR_DAC 0x04
532#define AD1986A_CLFE_DAC 0x05
533#define AD1986A_ADC 0x06
534
535static hda_nid_t ad1986a_dac_nids[3] = {
536 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
537};
Takashi Iwai985be542005-11-02 18:26:49 +0100538static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +0100539static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
541static struct hda_input_mux ad1986a_capture_source = {
542 .num_items = 7,
543 .items = {
544 { "Mic", 0x0 },
545 { "CD", 0x1 },
546 { "Aux", 0x3 },
547 { "Line", 0x4 },
548 { "Mix", 0x5 },
549 { "Mono", 0x6 },
550 { "Phone", 0x7 },
551 },
552};
553
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554
Takashi Iwai532d5382007-07-27 19:02:40 +0200555static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
556 .ops = &snd_hda_bind_vol,
557 .values = {
558 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
559 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
560 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
561 0
562 },
563};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
Takashi Iwai532d5382007-07-27 19:02:40 +0200565static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
566 .ops = &snd_hda_bind_sw,
567 .values = {
568 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
569 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
570 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
571 0
572 },
573};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
575/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 * mixers
577 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100578static struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200579 /*
580 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
581 */
582 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
583 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
585 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
586 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
587 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
588 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
589 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
590 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
591 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
592 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
593 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
594 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
595 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
596 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
597 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
598 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
599 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
600 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
601 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100602 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
604 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
605 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
606 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
607 {
608 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
609 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200610 .info = ad198x_mux_enum_info,
611 .get = ad198x_mux_enum_get,
612 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 },
614 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
615 { } /* end */
616};
617
Takashi Iwai9230d212006-03-13 13:49:49 +0100618/* additional mixers for 3stack mode */
619static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
620 {
621 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
622 .name = "Channel Mode",
623 .info = ad198x_ch_mode_info,
624 .get = ad198x_ch_mode_get,
625 .put = ad198x_ch_mode_put,
626 },
627 { } /* end */
628};
629
630/* laptop model - 2ch only */
631static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
632
Takashi Iwai20a45e82007-08-15 22:20:45 +0200633/* master controls both pins 0x1a and 0x1b */
634static struct hda_bind_ctls ad1986a_laptop_master_vol = {
635 .ops = &snd_hda_bind_vol,
636 .values = {
637 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
638 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
639 0,
640 },
641};
642
643static struct hda_bind_ctls ad1986a_laptop_master_sw = {
644 .ops = &snd_hda_bind_sw,
645 .values = {
646 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
647 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
648 0,
649 },
650};
651
Takashi Iwai9230d212006-03-13 13:49:49 +0100652static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
653 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
654 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200655 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
656 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100657 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
658 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
659 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
660 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
661 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
662 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
663 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
664 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100665 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100666 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100667 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
668 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
669 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
670 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
671 {
672 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
673 .name = "Capture Source",
674 .info = ad198x_mux_enum_info,
675 .get = ad198x_mux_enum_get,
676 .put = ad198x_mux_enum_put,
677 },
678 { } /* end */
679};
680
Takashi Iwai825aa972006-03-17 10:50:49 +0100681/* laptop-eapd model - 2ch only */
682
Takashi Iwai825aa972006-03-17 10:50:49 +0100683static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
684 .num_items = 3,
685 .items = {
686 { "Mic", 0x0 },
687 { "Internal Mic", 0x4 },
688 { "Mix", 0x5 },
689 },
690};
691
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100692static struct hda_input_mux ad1986a_automic_capture_source = {
693 .num_items = 2,
694 .items = {
695 { "Mic", 0x0 },
696 { "Mix", 0x5 },
697 },
698};
699
Takashi Iwai16d11a82009-06-24 14:07:53 +0200700static struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200701 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
702 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai16d11a82009-06-24 14:07:53 +0200703 { } /* end */
704};
705
706static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai825aa972006-03-17 10:50:49 +0100707 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
708 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100709 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
710 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
711 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
712 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
713 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
714 {
715 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
716 .name = "Capture Source",
717 .info = ad198x_mux_enum_info,
718 .get = ad198x_mux_enum_get,
719 .put = ad198x_mux_enum_put,
720 },
721 {
722 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
723 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100724 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwai1725b822008-11-21 02:25:48 +0100725 .info = ad198x_eapd_info,
726 .get = ad198x_eapd_get,
727 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100728 .private_value = 0x1b, /* port-D */
Takashi Iwai1725b822008-11-21 02:25:48 +0100729 },
730 { } /* end */
731};
732
Takashi Iwai16d11a82009-06-24 14:07:53 +0200733static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
734 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
735 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
Takashi Iwai825aa972006-03-17 10:50:49 +0100736 { } /* end */
737};
738
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100739/* re-connect the mic boost input according to the jack sensing */
740static void ad1986a_automic(struct hda_codec *codec)
741{
742 unsigned int present;
Takashi Iwaid56757a2009-11-18 08:00:14 +0100743 present = snd_hda_jack_detect(codec, 0x1f);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100744 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
745 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
Takashi Iwaid56757a2009-11-18 08:00:14 +0100746 present ? 0 : 2);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100747}
748
749#define AD1986A_MIC_EVENT 0x36
750
751static void ad1986a_automic_unsol_event(struct hda_codec *codec,
752 unsigned int res)
753{
754 if ((res >> 26) != AD1986A_MIC_EVENT)
755 return;
756 ad1986a_automic(codec);
757}
758
759static int ad1986a_automic_init(struct hda_codec *codec)
760{
761 ad198x_init(codec);
762 ad1986a_automic(codec);
763 return 0;
764}
765
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200766/* laptop-automute - 2ch only */
767
768static void ad1986a_update_hp(struct hda_codec *codec)
769{
770 struct ad198x_spec *spec = codec->spec;
771 unsigned int mute;
772
773 if (spec->jack_present)
774 mute = HDA_AMP_MUTE; /* mute internal speaker */
775 else
776 /* unmute internal speaker if necessary */
777 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
778 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
779 HDA_AMP_MUTE, mute);
780}
781
782static void ad1986a_hp_automute(struct hda_codec *codec)
783{
784 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200785
Takashi Iwaid56757a2009-11-18 08:00:14 +0100786 spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
Takashi Iwai03c405a2009-06-24 14:10:15 +0200787 if (spec->inv_jack_detect)
788 spec->jack_present = !spec->jack_present;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200789 ad1986a_update_hp(codec);
790}
791
792#define AD1986A_HP_EVENT 0x37
793
794static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
795{
796 if ((res >> 26) != AD1986A_HP_EVENT)
797 return;
798 ad1986a_hp_automute(codec);
799}
800
801static int ad1986a_hp_init(struct hda_codec *codec)
802{
803 ad198x_init(codec);
804 ad1986a_hp_automute(codec);
805 return 0;
806}
807
808/* bind hp and internal speaker mute (with plug check) */
809static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
810 struct snd_ctl_elem_value *ucontrol)
811{
812 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
813 long *valp = ucontrol->value.integer.value;
814 int change;
815
816 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
817 HDA_AMP_MUTE,
818 valp[0] ? 0 : HDA_AMP_MUTE);
819 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
820 HDA_AMP_MUTE,
821 valp[1] ? 0 : HDA_AMP_MUTE);
822 if (change)
823 ad1986a_update_hp(codec);
824 return change;
825}
826
Takashi Iwai16d11a82009-06-24 14:07:53 +0200827static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200828 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
829 {
830 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
831 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100832 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200833 .info = snd_hda_mixer_amp_switch_info,
834 .get = snd_hda_mixer_amp_switch_get,
835 .put = ad1986a_hp_master_sw_put,
836 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
837 },
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200838 { } /* end */
839};
840
Takashi Iwai16d11a82009-06-24 14:07:53 +0200841
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842/*
843 * initialization verbs
844 */
845static struct hda_verb ad1986a_init_verbs[] = {
846 /* Front, Surround, CLFE DAC; mute as default */
847 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
848 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
849 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
850 /* Downmix - off */
851 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
852 /* HP, Line-Out, Surround, CLFE selectors */
853 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
854 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
855 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
856 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
857 /* Mono selector */
858 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
859 /* Mic selector: Mic 1/2 pin */
860 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
861 /* Line-in selector: Line-in */
862 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
863 /* Mic 1/2 swap */
864 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
865 /* Record selector: mic */
866 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
867 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
868 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
869 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
870 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
871 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
872 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
873 /* PC beep */
874 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
875 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
876 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
877 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
878 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
879 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
880 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200881 /* HP Pin */
882 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
883 /* Front, Surround, CLFE Pins */
884 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
885 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
886 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
887 /* Mono Pin */
888 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
889 /* Mic Pin */
890 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
891 /* Line, Aux, CD, Beep-In Pin */
892 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
893 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
894 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
895 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
896 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 { } /* end */
898};
899
Takashi Iwai9230d212006-03-13 13:49:49 +0100900static struct hda_verb ad1986a_ch2_init[] = {
901 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200902 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
903 /* Line-in selectors */
904 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100905 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200906 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
907 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
908 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100909 { } /* end */
910};
911
912static struct hda_verb ad1986a_ch4_init[] = {
913 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200914 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
915 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100916 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200917 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
918 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100919 { } /* end */
920};
921
922static struct hda_verb ad1986a_ch6_init[] = {
923 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200924 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
925 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100926 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200927 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
928 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100929 { } /* end */
930};
931
932static struct hda_channel_mode ad1986a_modes[3] = {
933 { 2, ad1986a_ch2_init },
934 { 4, ad1986a_ch4_init },
935 { 6, ad1986a_ch6_init },
936};
937
Takashi Iwai825aa972006-03-17 10:50:49 +0100938/* eapd initialization */
939static struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +0100940 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa972006-03-17 10:50:49 +0100941 {}
942};
943
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100944static struct hda_verb ad1986a_automic_verbs[] = {
945 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
946 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
947 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
948 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
949 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
950 {}
951};
952
Tobin Davisf36090f2007-01-08 11:07:12 +0100953/* Ultra initialization */
954static struct hda_verb ad1986a_ultra_init[] = {
955 /* eapd initialization */
956 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
957 /* CLFE -> Mic in */
958 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
959 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
960 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
961 { } /* end */
962};
963
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200964/* pin sensing on HP jack */
965static struct hda_verb ad1986a_hp_init_verbs[] = {
966 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
967 {}
968};
969
Takashi Iwaic912e7a2009-06-24 14:14:34 +0200970static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
971 unsigned int res)
972{
973 switch (res >> 26) {
974 case AD1986A_HP_EVENT:
975 ad1986a_hp_automute(codec);
976 break;
977 case AD1986A_MIC_EVENT:
978 ad1986a_automic(codec);
979 break;
980 }
981}
982
983static int ad1986a_samsung_p50_init(struct hda_codec *codec)
984{
985 ad198x_init(codec);
986 ad1986a_hp_automute(codec);
987 ad1986a_automic(codec);
988 return 0;
989}
990
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200991
Takashi Iwai9230d212006-03-13 13:49:49 +0100992/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100993enum {
994 AD1986A_6STACK,
995 AD1986A_3STACK,
996 AD1986A_LAPTOP,
997 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200998 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +0100999 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +01001000 AD1986A_SAMSUNG,
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001001 AD1986A_SAMSUNG_P50,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001002 AD1986A_MODELS
1003};
Takashi Iwai9230d212006-03-13 13:49:49 +01001004
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001005static const char *ad1986a_models[AD1986A_MODELS] = {
1006 [AD1986A_6STACK] = "6stack",
1007 [AD1986A_3STACK] = "3stack",
1008 [AD1986A_LAPTOP] = "laptop",
1009 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001010 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +01001011 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +01001012 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001013 [AD1986A_SAMSUNG_P50] = "samsung-p50",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001014};
1015
1016static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
1017 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001018 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001019 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001020 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001021 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1022 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1023 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1024 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001025 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001026 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001027 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1028 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1029 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1030 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1031 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001032 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Takashi Iwai7db756f2007-12-24 14:36:09 +01001033 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
Tobin Davis18768992007-03-12 22:20:51 +01001034 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001035 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001036 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
Tobin Davisf36090f2007-01-08 11:07:12 +01001037 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001038 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001039 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001040 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001041 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001042 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001043 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001044 {}
1045};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046
Takashi Iwaicb53c622007-08-10 17:21:45 +02001047#ifdef CONFIG_SND_HDA_POWER_SAVE
1048static struct hda_amp_list ad1986a_loopbacks[] = {
1049 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1050 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1051 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1052 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1053 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1054 { } /* end */
1055};
1056#endif
1057
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001058static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1059{
Takashi Iwai2f334f92009-02-20 14:37:42 +01001060 unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001061 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1062}
1063
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064static int patch_ad1986a(struct hda_codec *codec)
1065{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001066 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001067 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001069 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 if (spec == NULL)
1071 return -ENOMEM;
1072
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 codec->spec = spec;
1074
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001075 err = snd_hda_attach_beep_device(codec, 0x19);
1076 if (err < 0) {
1077 ad198x_free(codec);
1078 return err;
1079 }
1080 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1081
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 spec->multiout.max_channels = 6;
1083 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1084 spec->multiout.dac_nids = ad1986a_dac_nids;
1085 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001086 spec->num_adc_nids = 1;
1087 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001088 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001089 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001090 spec->num_mixers = 1;
1091 spec->mixers[0] = ad1986a_mixers;
1092 spec->num_init_verbs = 1;
1093 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001094#ifdef CONFIG_SND_HDA_POWER_SAVE
1095 spec->loopback.amplist = ad1986a_loopbacks;
1096#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001097 spec->vmaster_nid = 0x1b;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01001098 spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001100 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101
Takashi Iwai9230d212006-03-13 13:49:49 +01001102 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001103 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1104 ad1986a_models,
1105 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001106 switch (board_config) {
1107 case AD1986A_3STACK:
1108 spec->num_mixers = 2;
1109 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001110 spec->num_init_verbs = 2;
1111 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001112 spec->channel_mode = ad1986a_modes;
1113 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001114 spec->need_dac_fix = 1;
1115 spec->multiout.max_channels = 2;
1116 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001117 break;
1118 case AD1986A_LAPTOP:
1119 spec->mixers[0] = ad1986a_laptop_mixers;
1120 spec->multiout.max_channels = 2;
1121 spec->multiout.num_dacs = 1;
1122 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1123 break;
Takashi Iwai825aa972006-03-17 10:50:49 +01001124 case AD1986A_LAPTOP_EAPD:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001125 spec->num_mixers = 3;
1126 spec->mixers[0] = ad1986a_laptop_master_mixers;
1127 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1128 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001129 spec->num_init_verbs = 2;
1130 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1131 spec->multiout.max_channels = 2;
1132 spec->multiout.num_dacs = 1;
1133 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1134 if (!is_jack_available(codec, 0x25))
1135 spec->multiout.dig_out_nid = 0;
1136 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1137 break;
1138 case AD1986A_SAMSUNG:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001139 spec->num_mixers = 2;
1140 spec->mixers[0] = ad1986a_laptop_master_mixers;
1141 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001142 spec->num_init_verbs = 3;
Takashi Iwai825aa972006-03-17 10:50:49 +01001143 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001144 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa972006-03-17 10:50:49 +01001145 spec->multiout.max_channels = 2;
1146 spec->multiout.num_dacs = 1;
1147 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001148 if (!is_jack_available(codec, 0x25))
1149 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001150 spec->input_mux = &ad1986a_automic_capture_source;
1151 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1152 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa972006-03-17 10:50:49 +01001153 break;
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001154 case AD1986A_SAMSUNG_P50:
1155 spec->num_mixers = 2;
1156 spec->mixers[0] = ad1986a_automute_master_mixers;
1157 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1158 spec->num_init_verbs = 4;
1159 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1160 spec->init_verbs[2] = ad1986a_automic_verbs;
1161 spec->init_verbs[3] = ad1986a_hp_init_verbs;
1162 spec->multiout.max_channels = 2;
1163 spec->multiout.num_dacs = 1;
1164 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1165 if (!is_jack_available(codec, 0x25))
1166 spec->multiout.dig_out_nid = 0;
1167 spec->input_mux = &ad1986a_automic_capture_source;
1168 codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
1169 codec->patch_ops.init = ad1986a_samsung_p50_init;
1170 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001171 case AD1986A_LAPTOP_AUTOMUTE:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001172 spec->num_mixers = 3;
1173 spec->mixers[0] = ad1986a_automute_master_mixers;
1174 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1175 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001176 spec->num_init_verbs = 3;
1177 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1178 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1179 spec->multiout.max_channels = 2;
1180 spec->multiout.num_dacs = 1;
1181 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001182 if (!is_jack_available(codec, 0x25))
1183 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001184 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1185 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1186 codec->patch_ops.init = ad1986a_hp_init;
Takashi Iwai03c405a2009-06-24 14:10:15 +02001187 /* Lenovo N100 seems to report the reversed bit
1188 * for HP jack-sensing
1189 */
1190 spec->inv_jack_detect = 1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001191 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001192 case AD1986A_ULTRA:
1193 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1194 spec->num_init_verbs = 2;
1195 spec->init_verbs[1] = ad1986a_ultra_init;
1196 spec->multiout.max_channels = 2;
1197 spec->multiout.num_dacs = 1;
1198 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1199 spec->multiout.dig_out_nid = 0;
1200 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001201 }
1202
Takashi Iwaid29240c2007-10-26 12:35:56 +02001203 /* AD1986A has a hardware problem that it can't share a stream
1204 * with multiple output pins. The copy of front to surrounds
1205 * causes noisy or silent outputs at a certain timing, e.g.
1206 * changing the volume.
1207 * So, let's disable the shared stream.
1208 */
1209 spec->multiout.no_share_stream = 1;
1210
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 return 0;
1212}
1213
1214/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001215 * AD1983 specific
1216 */
1217
1218#define AD1983_SPDIF_OUT 0x02
1219#define AD1983_DAC 0x03
1220#define AD1983_ADC 0x04
1221
1222static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001223static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001224static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001225
1226static struct hda_input_mux ad1983_capture_source = {
1227 .num_items = 4,
1228 .items = {
1229 { "Mic", 0x0 },
1230 { "Line", 0x1 },
1231 { "Mix", 0x2 },
1232 { "Mix Mono", 0x3 },
1233 },
1234};
1235
1236/*
1237 * SPDIF playback route
1238 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001239static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001240{
1241 static char *texts[] = { "PCM", "ADC" };
1242
1243 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1244 uinfo->count = 1;
1245 uinfo->value.enumerated.items = 2;
1246 if (uinfo->value.enumerated.item > 1)
1247 uinfo->value.enumerated.item = 1;
1248 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1249 return 0;
1250}
1251
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001252static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001253{
1254 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1255 struct ad198x_spec *spec = codec->spec;
1256
1257 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1258 return 0;
1259}
1260
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001261static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001262{
1263 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1264 struct ad198x_spec *spec = codec->spec;
1265
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001266 if (ucontrol->value.enumerated.item[0] > 1)
1267 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001268 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1269 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001270 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1271 AC_VERB_SET_CONNECT_SEL,
1272 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001273 return 1;
1274 }
1275 return 0;
1276}
1277
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001278static struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001279 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1280 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1281 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1282 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1283 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1284 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1285 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1286 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1287 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1288 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1289 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1290 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001291 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
1292 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1293 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1294 {
1295 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1296 .name = "Capture Source",
1297 .info = ad198x_mux_enum_info,
1298 .get = ad198x_mux_enum_get,
1299 .put = ad198x_mux_enum_put,
1300 },
1301 {
1302 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001303 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001304 .info = ad1983_spdif_route_info,
1305 .get = ad1983_spdif_route_get,
1306 .put = ad1983_spdif_route_put,
1307 },
1308 { } /* end */
1309};
1310
1311static struct hda_verb ad1983_init_verbs[] = {
1312 /* Front, HP, Mono; mute as default */
1313 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1314 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1315 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1316 /* Beep, PCM, Mic, Line-In: mute */
1317 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1318 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1319 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1320 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1321 /* Front, HP selectors; from Mix */
1322 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1323 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1324 /* Mono selector; from Mix */
1325 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1326 /* Mic selector; Mic */
1327 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1328 /* Line-in selector: Line-in */
1329 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1330 /* Mic boost: 0dB */
1331 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1332 /* Record selector: mic */
1333 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1334 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1335 /* SPDIF route: PCM */
1336 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1337 /* Front Pin */
1338 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1339 /* HP Pin */
1340 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1341 /* Mono Pin */
1342 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1343 /* Mic Pin */
1344 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1345 /* Line Pin */
1346 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1347 { } /* end */
1348};
1349
Takashi Iwaicb53c622007-08-10 17:21:45 +02001350#ifdef CONFIG_SND_HDA_POWER_SAVE
1351static struct hda_amp_list ad1983_loopbacks[] = {
1352 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1353 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1354 { } /* end */
1355};
1356#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001357
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001358static int patch_ad1983(struct hda_codec *codec)
1359{
1360 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001361 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001362
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001363 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001364 if (spec == NULL)
1365 return -ENOMEM;
1366
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001367 codec->spec = spec;
1368
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001369 err = snd_hda_attach_beep_device(codec, 0x10);
1370 if (err < 0) {
1371 ad198x_free(codec);
1372 return err;
1373 }
1374 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1375
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001376 spec->multiout.max_channels = 2;
1377 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1378 spec->multiout.dac_nids = ad1983_dac_nids;
1379 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001380 spec->num_adc_nids = 1;
1381 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001382 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001383 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001384 spec->num_mixers = 1;
1385 spec->mixers[0] = ad1983_mixers;
1386 spec->num_init_verbs = 1;
1387 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001388 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001389#ifdef CONFIG_SND_HDA_POWER_SAVE
1390 spec->loopback.amplist = ad1983_loopbacks;
1391#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001392 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001393
1394 codec->patch_ops = ad198x_patch_ops;
1395
1396 return 0;
1397}
1398
1399
1400/*
1401 * AD1981 HD specific
1402 */
1403
1404#define AD1981_SPDIF_OUT 0x02
1405#define AD1981_DAC 0x03
1406#define AD1981_ADC 0x04
1407
1408static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001409static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001410static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001411
1412/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
1413static struct hda_input_mux ad1981_capture_source = {
1414 .num_items = 7,
1415 .items = {
1416 { "Front Mic", 0x0 },
1417 { "Line", 0x1 },
1418 { "Mix", 0x2 },
1419 { "Mix Mono", 0x3 },
1420 { "CD", 0x4 },
1421 { "Mic", 0x6 },
1422 { "Aux", 0x7 },
1423 },
1424};
1425
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001426static struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001427 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1428 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1429 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1430 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1431 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1432 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1433 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1434 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1435 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1436 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1437 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1438 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1439 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1440 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1441 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1442 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1443 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1444 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001445 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
1446 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
1447 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1448 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1449 {
1450 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1451 .name = "Capture Source",
1452 .info = ad198x_mux_enum_info,
1453 .get = ad198x_mux_enum_get,
1454 .put = ad198x_mux_enum_put,
1455 },
1456 /* identical with AD1983 */
1457 {
1458 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001459 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001460 .info = ad1983_spdif_route_info,
1461 .get = ad1983_spdif_route_get,
1462 .put = ad1983_spdif_route_put,
1463 },
1464 { } /* end */
1465};
1466
1467static struct hda_verb ad1981_init_verbs[] = {
1468 /* Front, HP, Mono; mute as default */
1469 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1470 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1471 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1472 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1473 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1474 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1475 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1476 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1477 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1478 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1479 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1480 /* Front, HP selectors; from Mix */
1481 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1482 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1483 /* Mono selector; from Mix */
1484 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1485 /* Mic Mixer; select Front Mic */
1486 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1487 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1488 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001489 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1490 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001491 /* Record selector: Front mic */
1492 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1493 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1494 /* SPDIF route: PCM */
1495 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1496 /* Front Pin */
1497 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1498 /* HP Pin */
1499 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1500 /* Mono Pin */
1501 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1502 /* Front & Rear Mic Pins */
1503 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1504 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1505 /* Line Pin */
1506 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1507 /* Digital Beep */
1508 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1509 /* Line-Out as Input: disabled */
1510 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1511 { } /* end */
1512};
1513
Takashi Iwaicb53c622007-08-10 17:21:45 +02001514#ifdef CONFIG_SND_HDA_POWER_SAVE
1515static struct hda_amp_list ad1981_loopbacks[] = {
1516 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1517 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1518 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1519 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1520 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1521 { } /* end */
1522};
1523#endif
1524
Takashi Iwai18a815d2006-03-01 19:54:39 +01001525/*
1526 * Patch for HP nx6320
1527 *
Tobin Davis18768992007-03-12 22:20:51 +01001528 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001529 * speaker output enabled _and_ mute-LED off.
1530 */
1531
1532#define AD1981_HP_EVENT 0x37
1533#define AD1981_MIC_EVENT 0x38
1534
1535static struct hda_verb ad1981_hp_init_verbs[] = {
1536 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1537 /* pin sensing on HP and Mic jacks */
1538 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1539 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1540 {}
1541};
1542
1543/* turn on/off EAPD (+ mute HP) as a master switch */
1544static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1545 struct snd_ctl_elem_value *ucontrol)
1546{
1547 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1548 struct ad198x_spec *spec = codec->spec;
1549
1550 if (! ad198x_eapd_put(kcontrol, ucontrol))
1551 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001552 /* change speaker pin appropriately */
1553 snd_hda_codec_write(codec, 0x05, 0,
1554 AC_VERB_SET_PIN_WIDGET_CONTROL,
1555 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001556 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001557 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1558 HDA_AMP_MUTE,
1559 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001560 return 1;
1561}
1562
1563/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02001564static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
1565 .ops = &snd_hda_bind_vol,
1566 .values = {
1567 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1568 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1569 0
1570 },
1571};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001572
1573/* mute internal speaker if HP is plugged */
1574static void ad1981_hp_automute(struct hda_codec *codec)
1575{
1576 unsigned int present;
1577
Takashi Iwaid56757a2009-11-18 08:00:14 +01001578 present = snd_hda_jack_detect(codec, 0x06);
Takashi Iwai47fd8302007-08-10 17:11:07 +02001579 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1580 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001581}
1582
1583/* toggle input of built-in and mic jack appropriately */
1584static void ad1981_hp_automic(struct hda_codec *codec)
1585{
1586 static struct hda_verb mic_jack_on[] = {
1587 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1588 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1589 {}
1590 };
1591 static struct hda_verb mic_jack_off[] = {
1592 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1593 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1594 {}
1595 };
1596 unsigned int present;
1597
Takashi Iwaid56757a2009-11-18 08:00:14 +01001598 present = snd_hda_jack_detect(codec, 0x08);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001599 if (present)
1600 snd_hda_sequence_write(codec, mic_jack_on);
1601 else
1602 snd_hda_sequence_write(codec, mic_jack_off);
1603}
1604
1605/* unsolicited event for HP jack sensing */
1606static void ad1981_hp_unsol_event(struct hda_codec *codec,
1607 unsigned int res)
1608{
1609 res >>= 26;
1610 switch (res) {
1611 case AD1981_HP_EVENT:
1612 ad1981_hp_automute(codec);
1613 break;
1614 case AD1981_MIC_EVENT:
1615 ad1981_hp_automic(codec);
1616 break;
1617 }
1618}
1619
1620static struct hda_input_mux ad1981_hp_capture_source = {
1621 .num_items = 3,
1622 .items = {
1623 { "Mic", 0x0 },
1624 { "Docking-Station", 0x1 },
1625 { "Mix", 0x2 },
1626 },
1627};
1628
1629static struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001630 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001631 {
1632 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001633 .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
Takashi Iwai18a815d2006-03-01 19:54:39 +01001634 .name = "Master Playback Switch",
1635 .info = ad198x_eapd_info,
1636 .get = ad198x_eapd_get,
1637 .put = ad1981_hp_master_sw_put,
1638 .private_value = 0x05,
1639 },
1640 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1641 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1642#if 0
1643 /* FIXME: analog mic/line loopback doesn't work with my tests...
1644 * (although recording is OK)
1645 */
1646 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1647 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1648 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1649 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1650 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1651 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1652 /* FIXME: does this laptop have analog CD connection? */
1653 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1654 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1655#endif
1656 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1657 HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
1658 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1659 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1660 {
1661 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1662 .name = "Capture Source",
1663 .info = ad198x_mux_enum_info,
1664 .get = ad198x_mux_enum_get,
1665 .put = ad198x_mux_enum_put,
1666 },
1667 { } /* end */
1668};
1669
1670/* initialize jack-sensing, too */
1671static int ad1981_hp_init(struct hda_codec *codec)
1672{
1673 ad198x_init(codec);
1674 ad1981_hp_automute(codec);
1675 ad1981_hp_automic(codec);
1676 return 0;
1677}
1678
Tobin Davis18768992007-03-12 22:20:51 +01001679/* configuration for Toshiba Laptops */
1680static struct hda_verb ad1981_toshiba_init_verbs[] = {
1681 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1682 /* pin sensing on HP and Mic jacks */
1683 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1684 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1685 {}
1686};
1687
1688static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
1689 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1690 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1691 { }
1692};
1693
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001694/* configuration for Lenovo Thinkpad T60 */
1695static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
1696 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1697 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1698 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1699 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1700 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1701 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1702 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1703 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1704 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1705 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1706 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1707 {
1708 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1709 .name = "Capture Source",
1710 .info = ad198x_mux_enum_info,
1711 .get = ad198x_mux_enum_get,
1712 .put = ad198x_mux_enum_put,
1713 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001714 /* identical with AD1983 */
1715 {
1716 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1717 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1718 .info = ad1983_spdif_route_info,
1719 .get = ad1983_spdif_route_get,
1720 .put = ad1983_spdif_route_put,
1721 },
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001722 { } /* end */
1723};
1724
1725static struct hda_input_mux ad1981_thinkpad_capture_source = {
1726 .num_items = 3,
1727 .items = {
1728 { "Mic", 0x0 },
1729 { "Mix", 0x2 },
1730 { "CD", 0x4 },
1731 },
1732};
1733
Takashi Iwai18a815d2006-03-01 19:54:39 +01001734/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001735enum {
1736 AD1981_BASIC,
1737 AD1981_HP,
1738 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001739 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001740 AD1981_MODELS
1741};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001742
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001743static const char *ad1981_models[AD1981_MODELS] = {
1744 [AD1981_HP] = "hp",
1745 [AD1981_THINKPAD] = "thinkpad",
1746 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001747 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001748};
1749
1750static struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001751 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001752 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001753 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001754 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001755 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001756 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001757 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001758 /* HP nx6320 (reversed SSID, H/W bug) */
1759 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001760 {}
1761};
1762
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001763static int patch_ad1981(struct hda_codec *codec)
1764{
1765 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001766 int err, board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001767
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001768 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001769 if (spec == NULL)
1770 return -ENOMEM;
1771
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001772 codec->spec = spec;
1773
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001774 err = snd_hda_attach_beep_device(codec, 0x10);
1775 if (err < 0) {
1776 ad198x_free(codec);
1777 return err;
1778 }
1779 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
1780
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001781 spec->multiout.max_channels = 2;
1782 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1783 spec->multiout.dac_nids = ad1981_dac_nids;
1784 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001785 spec->num_adc_nids = 1;
1786 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001787 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001788 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001789 spec->num_mixers = 1;
1790 spec->mixers[0] = ad1981_mixers;
1791 spec->num_init_verbs = 1;
1792 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001793 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001794#ifdef CONFIG_SND_HDA_POWER_SAVE
1795 spec->loopback.amplist = ad1981_loopbacks;
1796#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001797 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001798
1799 codec->patch_ops = ad198x_patch_ops;
1800
Takashi Iwai18a815d2006-03-01 19:54:39 +01001801 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001802 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1803 ad1981_models,
1804 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001805 switch (board_config) {
1806 case AD1981_HP:
1807 spec->mixers[0] = ad1981_hp_mixers;
1808 spec->num_init_verbs = 2;
1809 spec->init_verbs[1] = ad1981_hp_init_verbs;
1810 spec->multiout.dig_out_nid = 0;
1811 spec->input_mux = &ad1981_hp_capture_source;
1812
1813 codec->patch_ops.init = ad1981_hp_init;
1814 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
Daniel T Chen01f59662009-12-13 16:22:58 -05001815 /* set the upper-limit for mixer amp to 0dB for avoiding the
1816 * possible damage by overloading
1817 */
1818 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
1819 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
1820 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
1821 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
1822 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai18a815d2006-03-01 19:54:39 +01001823 break;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001824 case AD1981_THINKPAD:
1825 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001826 spec->input_mux = &ad1981_thinkpad_capture_source;
1827 break;
Tobin Davis18768992007-03-12 22:20:51 +01001828 case AD1981_TOSHIBA:
1829 spec->mixers[0] = ad1981_hp_mixers;
1830 spec->mixers[1] = ad1981_toshiba_mixers;
1831 spec->num_init_verbs = 2;
1832 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1833 spec->multiout.dig_out_nid = 0;
1834 spec->input_mux = &ad1981_hp_capture_source;
1835 codec->patch_ops.init = ad1981_hp_init;
1836 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1837 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001838 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001839 return 0;
1840}
1841
1842
1843/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001844 * AD1988
1845 *
1846 * Output pins and routes
1847 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001848 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001849 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1850 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1851 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1852 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1853 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1854 * port-F 0x16 (mute) <- 0x2a <- 06
1855 * port-G 0x24 (mute) <- 0x27 <- 05
1856 * port-H 0x25 (mute) <- 0x28 <- 0a
1857 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1858 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001859 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1860 * (*) 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 +01001861 *
1862 * Input pins and routes
1863 *
1864 * pin boost mix input # / adc input #
1865 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1866 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1867 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1868 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1869 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1870 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1871 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1872 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1873 *
1874 *
1875 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001876 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001877 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001878 *
1879 * Inputs of Analog Mix (0x20)
1880 * 0:Port-B (front mic)
1881 * 1:Port-C/G/H (line-in)
1882 * 2:Port-A
1883 * 3:Port-D (line-in/2)
1884 * 4:Port-E/G/H (mic-in)
1885 * 5:Port-F (mic2-in)
1886 * 6:CD
1887 * 7:Beep
1888 *
1889 * ADC selection
1890 * 0:Port-A
1891 * 1:Port-B (front mic-in)
1892 * 2:Port-C (line-in)
1893 * 3:Port-F (mic2-in)
1894 * 4:Port-E (mic-in)
1895 * 5:CD
1896 * 6:Port-G
1897 * 7:Port-H
1898 * 8:Port-D (line-in/2)
1899 * 9:Mix
1900 *
1901 * Proposed pin assignments by the datasheet
1902 *
1903 * 6-stack
1904 * Port-A front headphone
1905 * B front mic-in
1906 * C rear line-in
1907 * D rear front-out
1908 * E rear mic-in
1909 * F rear surround
1910 * G rear CLFE
1911 * H rear side
1912 *
1913 * 3-stack
1914 * Port-A front headphone
1915 * B front mic
1916 * C rear line-in/surround
1917 * D rear front-out
1918 * E rear mic-in/CLFE
1919 *
1920 * laptop
1921 * Port-A headphone
1922 * B mic-in
1923 * C docking station
1924 * D internal speaker (with EAPD)
1925 * E/F quad mic array
1926 */
1927
1928
1929/* models */
1930enum {
1931 AD1988_6STACK,
1932 AD1988_6STACK_DIG,
1933 AD1988_3STACK,
1934 AD1988_3STACK_DIG,
1935 AD1988_LAPTOP,
1936 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01001937 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001938 AD1988_MODEL_LAST,
1939};
1940
Takashi Iwaid32410b12005-11-24 16:06:23 +01001941/* reivision id to check workarounds */
1942#define AD1988A_REV2 0x100200
1943
Takashi Iwai1a806f42006-07-03 15:58:16 +02001944#define is_rev2(codec) \
1945 ((codec)->vendor_id == 0x11d41988 && \
1946 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001947
1948/*
1949 * mixers
1950 */
1951
Takashi Iwaid32410b12005-11-24 16:06:23 +01001952static hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001953 0x04, 0x06, 0x05, 0x0a
1954};
1955
Takashi Iwaid32410b12005-11-24 16:06:23 +01001956static hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001957 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01001958};
1959
1960/* for AD1988A revision-2, DAC2-4 are swapped */
1961static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
1962 0x04, 0x05, 0x0a, 0x06
1963};
1964
1965static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001966 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01001967};
1968
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001969static hda_nid_t ad1988_adc_nids[3] = {
1970 0x08, 0x09, 0x0f
1971};
1972
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001973static hda_nid_t ad1988_capsrc_nids[3] = {
1974 0x0c, 0x0d, 0x0e
1975};
1976
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001977#define AD1988_SPDIF_OUT 0x02
1978#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001979#define AD1988_SPDIF_IN 0x07
1980
Takashi Iwai3a08e302009-02-13 11:37:08 +01001981static hda_nid_t ad1989b_slave_dig_outs[] = {
1982 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001983};
1984
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001985static struct hda_input_mux ad1988_6stack_capture_source = {
1986 .num_items = 5,
1987 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001988 { "Front Mic", 0x1 }, /* port-B */
1989 { "Line", 0x2 }, /* port-C */
1990 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001991 { "CD", 0x5 },
1992 { "Mix", 0x9 },
1993 },
1994};
1995
1996static struct hda_input_mux ad1988_laptop_capture_source = {
1997 .num_items = 3,
1998 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001999 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002000 { "CD", 0x5 },
2001 { "Mix", 0x9 },
2002 },
2003};
2004
2005/*
2006 */
2007static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
2008 struct snd_ctl_elem_info *uinfo)
2009{
2010 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2011 struct ad198x_spec *spec = codec->spec;
2012 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
2013 spec->num_channel_mode);
2014}
2015
2016static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
2017 struct snd_ctl_elem_value *ucontrol)
2018{
2019 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2020 struct ad198x_spec *spec = codec->spec;
2021 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
2022 spec->num_channel_mode, spec->multiout.max_channels);
2023}
2024
2025static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
2026 struct snd_ctl_elem_value *ucontrol)
2027{
2028 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2029 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002030 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
2031 spec->num_channel_mode,
2032 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02002033 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02002034 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002035 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002036}
2037
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002038/* 6-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002039static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002040 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2041 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
2042 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2043 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
2044 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002045 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002046};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002047
Takashi Iwaid32410b12005-11-24 16:06:23 +01002048static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
2049 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2050 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2051 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2052 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2053 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002054 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002055};
2056
2057static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002058 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2059 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2060 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2061 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2062 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2063 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2064 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2065
2066 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2067 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2068 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2069 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2070 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2071 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2072 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2073 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2074
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002075 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002076 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2077
2078 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2079 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2080
2081 { } /* end */
2082};
2083
2084/* 3-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002085static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002086 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002087 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002088 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2089 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002090 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002091};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002092
Takashi Iwaid32410b12005-11-24 16:06:23 +01002093static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
2094 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002095 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2096 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2097 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002098 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002099};
2100
2101static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002102 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002103 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2104 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2105 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002106 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2107 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2108
2109 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2110 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2111 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2112 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2113 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2114 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2115 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2116 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2117
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002118 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002119 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2120
2121 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2122 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2123 {
2124 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2125 .name = "Channel Mode",
2126 .info = ad198x_ch_mode_info,
2127 .get = ad198x_ch_mode_get,
2128 .put = ad198x_ch_mode_put,
2129 },
2130
2131 { } /* end */
2132};
2133
2134/* laptop mode */
2135static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
2136 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2137 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2138 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2139
2140 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2141 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2142 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2143 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2144 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2145 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2146
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002147 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002148 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2149
2150 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2151
2152 {
2153 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2154 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002155 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01002156 .info = ad198x_eapd_info,
2157 .get = ad198x_eapd_get,
2158 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01002159 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002160 },
2161
2162 { } /* end */
2163};
2164
2165/* capture */
2166static struct snd_kcontrol_new ad1988_capture_mixers[] = {
2167 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2168 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2169 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2170 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2171 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2172 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2173 {
2174 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2175 /* The multiple "Capture Source" controls confuse alsamixer
2176 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002177 */
2178 /* .name = "Capture Source", */
2179 .name = "Input Source",
2180 .count = 3,
2181 .info = ad198x_mux_enum_info,
2182 .get = ad198x_mux_enum_get,
2183 .put = ad198x_mux_enum_put,
2184 },
2185 { } /* end */
2186};
2187
2188static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2189 struct snd_ctl_elem_info *uinfo)
2190{
2191 static char *texts[] = {
2192 "PCM", "ADC1", "ADC2", "ADC3"
2193 };
2194 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2195 uinfo->count = 1;
2196 uinfo->value.enumerated.items = 4;
2197 if (uinfo->value.enumerated.item >= 4)
2198 uinfo->value.enumerated.item = 3;
2199 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2200 return 0;
2201}
2202
2203static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2204 struct snd_ctl_elem_value *ucontrol)
2205{
2206 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2207 unsigned int sel;
2208
Takashi Iwaibddcf542007-07-24 18:04:05 +02002209 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2210 AC_AMP_GET_INPUT);
2211 if (!(sel & 0x80))
2212 ucontrol->value.enumerated.item[0] = 0;
2213 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002214 sel = snd_hda_codec_read(codec, 0x0b, 0,
2215 AC_VERB_GET_CONNECT_SEL, 0);
2216 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002217 sel++;
2218 else
2219 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002220 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002221 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002222 return 0;
2223}
2224
2225static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2226 struct snd_ctl_elem_value *ucontrol)
2227{
2228 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002229 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002230 int change;
2231
Takashi Iwai35b26722007-05-05 12:17:17 +02002232 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002233 if (val > 3)
2234 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002235 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002236 sel = snd_hda_codec_read(codec, 0x1d, 0,
2237 AC_VERB_GET_AMP_GAIN_MUTE,
2238 AC_AMP_GET_INPUT);
2239 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002240 if (change) {
2241 snd_hda_codec_write_cache(codec, 0x1d, 0,
2242 AC_VERB_SET_AMP_GAIN_MUTE,
2243 AMP_IN_UNMUTE(0));
2244 snd_hda_codec_write_cache(codec, 0x1d, 0,
2245 AC_VERB_SET_AMP_GAIN_MUTE,
2246 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002247 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002248 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002249 sel = snd_hda_codec_read(codec, 0x1d, 0,
2250 AC_VERB_GET_AMP_GAIN_MUTE,
2251 AC_AMP_GET_INPUT | 0x01);
2252 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002253 if (change) {
2254 snd_hda_codec_write_cache(codec, 0x1d, 0,
2255 AC_VERB_SET_AMP_GAIN_MUTE,
2256 AMP_IN_MUTE(0));
2257 snd_hda_codec_write_cache(codec, 0x1d, 0,
2258 AC_VERB_SET_AMP_GAIN_MUTE,
2259 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002260 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002261 sel = snd_hda_codec_read(codec, 0x0b, 0,
2262 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2263 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002264 if (change)
2265 snd_hda_codec_write_cache(codec, 0x0b, 0,
2266 AC_VERB_SET_CONNECT_SEL,
2267 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002268 }
2269 return change;
2270}
2271
2272static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
2273 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2274 {
2275 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2276 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002277 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002278 .info = ad1988_spdif_playback_source_info,
2279 .get = ad1988_spdif_playback_source_get,
2280 .put = ad1988_spdif_playback_source_put,
2281 },
2282 { } /* end */
2283};
2284
2285static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
2286 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2287 { } /* end */
2288};
2289
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002290static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
2291 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002292 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002293 { } /* end */
2294};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002295
2296/*
2297 * initialization verbs
2298 */
2299
2300/*
2301 * for 6-stack (+dig)
2302 */
2303static struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002304 /* Front, Surround, CLFE, side DAC; unmute as default */
2305 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2306 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2307 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2308 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002309 /* Port-A front headphon path */
2310 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2311 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2312 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2313 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2314 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2315 /* Port-D line-out path */
2316 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2317 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2318 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2319 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2320 /* Port-F surround path */
2321 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2322 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2323 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2324 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2325 /* Port-G CLFE path */
2326 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2327 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2328 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2329 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2330 /* Port-H side path */
2331 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2332 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2333 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2334 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2335 /* Mono out path */
2336 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2337 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2338 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2339 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2340 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2341 /* Port-B front mic-in path */
2342 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2343 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2344 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2345 /* Port-C line-in path */
2346 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2347 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2348 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2349 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2350 /* Port-E mic-in path */
2351 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2352 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2353 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2354 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002355 /* Analog CD Input */
2356 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002357 /* Analog Mix output amp */
2358 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002359
2360 { }
2361};
2362
2363static struct hda_verb ad1988_capture_init_verbs[] = {
2364 /* mute analog mix */
2365 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2366 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2367 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2368 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2369 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2370 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2371 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2372 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2373 /* select ADCs - front-mic */
2374 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2375 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2376 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002377
2378 { }
2379};
2380
2381static struct hda_verb ad1988_spdif_init_verbs[] = {
2382 /* SPDIF out sel */
2383 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2384 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2385 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002386 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002387 /* SPDIF out pin */
2388 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002389
2390 { }
2391};
2392
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002393/* AD1989 has no ADC -> SPDIF route */
2394static struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002395 /* SPDIF-1 out pin */
2396 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002397 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002398 /* SPDIF-2/HDMI out pin */
2399 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2400 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002401 { }
2402};
2403
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002404/*
2405 * verbs for 3stack (+dig)
2406 */
2407static struct hda_verb ad1988_3stack_ch2_init[] = {
2408 /* set port-C to line-in */
2409 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2410 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2411 /* set port-E to mic-in */
2412 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2413 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2414 { } /* end */
2415};
2416
2417static struct hda_verb ad1988_3stack_ch6_init[] = {
2418 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002419 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002420 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002421 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002422 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002423 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002424 { } /* end */
2425};
2426
2427static struct hda_channel_mode ad1988_3stack_modes[2] = {
2428 { 2, ad1988_3stack_ch2_init },
2429 { 6, ad1988_3stack_ch6_init },
2430};
2431
2432static struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002433 /* Front, Surround, CLFE, side DAC; unmute as default */
2434 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2435 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2436 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2437 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002438 /* Port-A front headphon path */
2439 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2440 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2441 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2442 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2443 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2444 /* Port-D line-out path */
2445 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2446 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2447 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2448 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2449 /* Mono out path */
2450 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2451 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2452 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2453 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2454 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2455 /* Port-B front mic-in path */
2456 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2457 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2458 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002459 /* Port-C line-in/surround path - 6ch mode as default */
2460 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2461 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002462 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002463 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002464 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002465 /* Port-E mic-in/CLFE path - 6ch mode as default */
2466 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2467 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002468 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002469 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002470 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2471 /* mute analog mix */
2472 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2473 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2474 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2475 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2476 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2477 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2478 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2479 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2480 /* select ADCs - front-mic */
2481 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2482 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2483 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002484 /* Analog Mix output amp */
2485 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002486 { }
2487};
2488
2489/*
2490 * verbs for laptop mode (+dig)
2491 */
2492static struct hda_verb ad1988_laptop_hp_on[] = {
2493 /* unmute port-A and mute port-D */
2494 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2495 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2496 { } /* end */
2497};
2498static struct hda_verb ad1988_laptop_hp_off[] = {
2499 /* mute port-A and unmute port-D */
2500 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2501 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2502 { } /* end */
2503};
2504
2505#define AD1988_HP_EVENT 0x01
2506
2507static struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002508 /* Front, Surround, CLFE, side DAC; unmute as default */
2509 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2510 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2511 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2512 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002513 /* Port-A front headphon path */
2514 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2515 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2516 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2517 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2518 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2519 /* unsolicited event for pin-sense */
2520 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2521 /* Port-D line-out path + EAPD */
2522 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2523 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2524 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2525 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2526 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2527 /* Mono out path */
2528 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2529 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2530 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2531 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2532 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2533 /* Port-B mic-in path */
2534 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2535 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2536 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2537 /* Port-C docking station - try to output */
2538 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2539 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2540 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2541 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2542 /* mute analog mix */
2543 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2544 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2545 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2546 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2547 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2548 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2549 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2550 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2551 /* select ADCs - mic */
2552 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2553 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2554 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002555 /* Analog Mix output amp */
2556 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002557 { }
2558};
2559
2560static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2561{
2562 if ((res >> 26) != AD1988_HP_EVENT)
2563 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01002564 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002565 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2566 else
2567 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2568}
2569
Takashi Iwaicb53c622007-08-10 17:21:45 +02002570#ifdef CONFIG_SND_HDA_POWER_SAVE
2571static struct hda_amp_list ad1988_loopbacks[] = {
2572 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2573 { 0x20, HDA_INPUT, 1 }, /* Line */
2574 { 0x20, HDA_INPUT, 4 }, /* Mic */
2575 { 0x20, HDA_INPUT, 6 }, /* CD */
2576 { } /* end */
2577};
2578#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002579
2580/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002581 * Automatic parse of I/O pins from the BIOS configuration
2582 */
2583
Takashi Iwaid32410b12005-11-24 16:06:23 +01002584enum {
2585 AD_CTL_WIDGET_VOL,
2586 AD_CTL_WIDGET_MUTE,
2587 AD_CTL_BIND_MUTE,
2588};
2589static struct snd_kcontrol_new ad1988_control_templates[] = {
2590 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2591 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2592 HDA_BIND_MUTE(NULL, 0, 0, 0),
2593};
2594
2595/* add dynamic controls */
2596static int add_control(struct ad198x_spec *spec, int type, const char *name,
2597 unsigned long val)
2598{
2599 struct snd_kcontrol_new *knew;
2600
Takashi Iwai603c4012008-07-30 15:01:44 +02002601 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2602 knew = snd_array_new(&spec->kctls);
2603 if (!knew)
2604 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002605 *knew = ad1988_control_templates[type];
2606 knew->name = kstrdup(name, GFP_KERNEL);
2607 if (! knew->name)
2608 return -ENOMEM;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +01002609 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01002610 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002611 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002612 return 0;
2613}
2614
2615#define AD1988_PIN_CD_NID 0x18
2616#define AD1988_PIN_BEEP_NID 0x10
2617
2618static hda_nid_t ad1988_mixer_nids[8] = {
2619 /* A B C D E F G H */
2620 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2621};
2622
2623static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2624{
2625 static hda_nid_t idx_to_dac[8] = {
2626 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002627 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002628 };
2629 static hda_nid_t idx_to_dac_rev2[8] = {
2630 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002631 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002632 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002633 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002634 return idx_to_dac_rev2[idx];
2635 else
2636 return idx_to_dac[idx];
2637}
2638
2639static hda_nid_t ad1988_boost_nids[8] = {
2640 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2641};
2642
2643static int ad1988_pin_idx(hda_nid_t nid)
2644{
2645 static hda_nid_t ad1988_io_pins[8] = {
2646 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2647 };
2648 int i;
2649 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2650 if (ad1988_io_pins[i] == nid)
2651 return i;
2652 return 0; /* should be -1 */
2653}
2654
2655static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2656{
2657 static int loopback_idx[8] = {
2658 2, 0, 1, 3, 4, 5, 1, 4
2659 };
2660 switch (nid) {
2661 case AD1988_PIN_CD_NID:
2662 return 6;
2663 default:
2664 return loopback_idx[ad1988_pin_idx(nid)];
2665 }
2666}
2667
2668static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2669{
2670 static int adc_idx[8] = {
2671 0, 1, 2, 8, 4, 3, 6, 7
2672 };
2673 switch (nid) {
2674 case AD1988_PIN_CD_NID:
2675 return 5;
2676 default:
2677 return adc_idx[ad1988_pin_idx(nid)];
2678 }
2679}
2680
2681/* fill in the dac_nids table from the parsed pin configuration */
2682static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2683 const struct auto_pin_cfg *cfg)
2684{
2685 struct ad198x_spec *spec = codec->spec;
2686 int i, idx;
2687
2688 spec->multiout.dac_nids = spec->private_dac_nids;
2689
2690 /* check the pins hardwired to audio widget */
2691 for (i = 0; i < cfg->line_outs; i++) {
2692 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2693 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2694 }
2695 spec->multiout.num_dacs = cfg->line_outs;
2696 return 0;
2697}
2698
2699/* add playback controls from the parsed DAC table */
2700static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2701 const struct auto_pin_cfg *cfg)
2702{
2703 char name[32];
2704 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2705 hda_nid_t nid;
2706 int i, err;
2707
2708 for (i = 0; i < cfg->line_outs; i++) {
2709 hda_nid_t dac = spec->multiout.dac_nids[i];
2710 if (! dac)
2711 continue;
2712 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2713 if (i == 2) {
2714 /* Center/LFE */
2715 err = add_control(spec, AD_CTL_WIDGET_VOL,
2716 "Center Playback Volume",
2717 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2718 if (err < 0)
2719 return err;
2720 err = add_control(spec, AD_CTL_WIDGET_VOL,
2721 "LFE Playback Volume",
2722 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2723 if (err < 0)
2724 return err;
2725 err = add_control(spec, AD_CTL_BIND_MUTE,
2726 "Center Playback Switch",
2727 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2728 if (err < 0)
2729 return err;
2730 err = add_control(spec, AD_CTL_BIND_MUTE,
2731 "LFE Playback Switch",
2732 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2733 if (err < 0)
2734 return err;
2735 } else {
2736 sprintf(name, "%s Playback Volume", chname[i]);
2737 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2738 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2739 if (err < 0)
2740 return err;
2741 sprintf(name, "%s Playback Switch", chname[i]);
2742 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2743 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2744 if (err < 0)
2745 return err;
2746 }
2747 }
2748 return 0;
2749}
2750
2751/* add playback controls for speaker and HP outputs */
2752static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2753 const char *pfx)
2754{
2755 struct ad198x_spec *spec = codec->spec;
2756 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02002757 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002758 char name[32];
2759
2760 if (! pin)
2761 return 0;
2762
2763 idx = ad1988_pin_idx(pin);
2764 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02002765 /* check whether the corresponding DAC was already taken */
2766 for (i = 0; i < spec->autocfg.line_outs; i++) {
2767 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2768 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
2769 if (dac == nid)
2770 break;
2771 }
2772 if (i >= spec->autocfg.line_outs) {
2773 /* specify the DAC as the extra output */
2774 if (!spec->multiout.hp_nid)
2775 spec->multiout.hp_nid = nid;
2776 else
2777 spec->multiout.extra_out_nid[0] = nid;
2778 /* control HP volume/switch on the output mixer amp */
2779 sprintf(name, "%s Playback Volume", pfx);
2780 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2781 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2782 if (err < 0)
2783 return err;
2784 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01002785 nid = ad1988_mixer_nids[idx];
2786 sprintf(name, "%s Playback Switch", pfx);
2787 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2788 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2789 return err;
2790 return 0;
2791}
2792
2793/* create input playback/capture controls for the given pin */
2794static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2795 const char *ctlname, int boost)
2796{
2797 char name[32];
2798 int err, idx;
2799
2800 sprintf(name, "%s Playback Volume", ctlname);
2801 idx = ad1988_pin_to_loopback_idx(pin);
2802 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2803 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2804 return err;
2805 sprintf(name, "%s Playback Switch", ctlname);
2806 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2807 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2808 return err;
2809 if (boost) {
2810 hda_nid_t bnid;
2811 idx = ad1988_pin_idx(pin);
2812 bnid = ad1988_boost_nids[idx];
2813 if (bnid) {
2814 sprintf(name, "%s Boost", ctlname);
2815 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2816 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2817
2818 }
2819 }
2820 return 0;
2821}
2822
2823/* create playback/capture controls for input pins */
2824static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2825 const struct auto_pin_cfg *cfg)
2826{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002827 struct hda_input_mux *imux = &spec->private_imux;
2828 int i, err;
2829
2830 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002831 err = new_analog_input(spec, cfg->input_pins[i],
2832 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002833 i <= AUTO_PIN_FRONT_MIC);
2834 if (err < 0)
2835 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002836 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002837 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2838 imux->num_items++;
2839 }
2840 imux->items[imux->num_items].label = "Mix";
2841 imux->items[imux->num_items].index = 9;
2842 imux->num_items++;
2843
2844 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2845 "Analog Mix Playback Volume",
2846 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2847 return err;
2848 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2849 "Analog Mix Playback Switch",
2850 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2851 return err;
2852
2853 return 0;
2854}
2855
2856static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2857 hda_nid_t nid, int pin_type,
2858 int dac_idx)
2859{
2860 /* set as output */
2861 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2862 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2863 switch (nid) {
2864 case 0x11: /* port-A - DAC 04 */
2865 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2866 break;
2867 case 0x14: /* port-B - DAC 06 */
2868 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2869 break;
2870 case 0x15: /* port-C - DAC 05 */
2871 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2872 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002873 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002874 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2875 break;
2876 case 0x13: /* mono - DAC 04 */
2877 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2878 break;
2879 }
2880}
2881
2882static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2883{
2884 struct ad198x_spec *spec = codec->spec;
2885 int i;
2886
2887 for (i = 0; i < spec->autocfg.line_outs; i++) {
2888 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2889 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2890 }
2891}
2892
2893static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2894{
2895 struct ad198x_spec *spec = codec->spec;
2896 hda_nid_t pin;
2897
Takashi Iwai82bc9552006-03-21 11:24:42 +01002898 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002899 if (pin) /* connect to front */
2900 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002901 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002902 if (pin) /* connect to front */
2903 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2904}
2905
2906static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2907{
2908 struct ad198x_spec *spec = codec->spec;
2909 int i, idx;
2910
2911 for (i = 0; i < AUTO_PIN_LAST; i++) {
2912 hda_nid_t nid = spec->autocfg.input_pins[i];
2913 if (! nid)
2914 continue;
2915 switch (nid) {
2916 case 0x15: /* port-C */
2917 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2918 break;
2919 case 0x17: /* port-E */
2920 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2921 break;
2922 }
2923 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2924 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2925 if (nid != AD1988_PIN_CD_NID)
2926 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
2927 AMP_OUT_MUTE);
2928 idx = ad1988_pin_idx(nid);
2929 if (ad1988_boost_nids[idx])
2930 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
2931 AC_VERB_SET_AMP_GAIN_MUTE,
2932 AMP_OUT_ZERO);
2933 }
2934}
2935
2936/* parse the BIOS configuration and set up the alc_spec */
2937/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
2938static int ad1988_parse_auto_config(struct hda_codec *codec)
2939{
2940 struct ad198x_spec *spec = codec->spec;
2941 int err;
2942
Kailang Yangdf694da2005-12-05 19:42:22 +01002943 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002944 return err;
2945 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
2946 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002947 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002948 return 0; /* can't find valid BIOS pin config */
2949 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01002950 (err = ad1988_auto_create_extra_out(codec,
2951 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002952 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002953 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002954 "Headphone")) < 0 ||
2955 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
2956 return err;
2957
2958 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2959
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002960 if (spec->autocfg.dig_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002961 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2962 if (spec->autocfg.dig_in_pin)
2963 spec->dig_in_nid = AD1988_SPDIF_IN;
2964
Takashi Iwai603c4012008-07-30 15:01:44 +02002965 if (spec->kctls.list)
2966 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002967
2968 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
2969
2970 spec->input_mux = &spec->private_imux;
2971
2972 return 1;
2973}
2974
2975/* init callback for auto-configuration model -- overriding the default init */
2976static int ad1988_auto_init(struct hda_codec *codec)
2977{
2978 ad198x_init(codec);
2979 ad1988_auto_init_multi_out(codec);
2980 ad1988_auto_init_extra_out(codec);
2981 ad1988_auto_init_analog_input(codec);
2982 return 0;
2983}
2984
2985
2986/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002987 */
2988
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002989static const char *ad1988_models[AD1988_MODEL_LAST] = {
2990 [AD1988_6STACK] = "6stack",
2991 [AD1988_6STACK_DIG] = "6stack-dig",
2992 [AD1988_3STACK] = "3stack",
2993 [AD1988_3STACK_DIG] = "3stack-dig",
2994 [AD1988_LAPTOP] = "laptop",
2995 [AD1988_LAPTOP_DIG] = "laptop-dig",
2996 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002997};
2998
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002999static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01003000 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01003001 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02003002 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07003003 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003004 {}
3005};
3006
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003007static int patch_ad1988(struct hda_codec *codec)
3008{
3009 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003010 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003011
3012 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3013 if (spec == NULL)
3014 return -ENOMEM;
3015
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003016 codec->spec = spec;
3017
Takashi Iwai1a806f42006-07-03 15:58:16 +02003018 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003019 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
3020
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003021 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003022 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003023 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02003024 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3025 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003026 board_config = AD1988_AUTO;
3027 }
3028
3029 if (board_config == AD1988_AUTO) {
3030 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003031 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003032 if (err < 0) {
3033 ad198x_free(codec);
3034 return err;
3035 } else if (! err) {
3036 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
3037 board_config = AD1988_6STACK;
3038 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003039 }
3040
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003041 err = snd_hda_attach_beep_device(codec, 0x10);
3042 if (err < 0) {
3043 ad198x_free(codec);
3044 return err;
3045 }
3046 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3047
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003048 switch (board_config) {
3049 case AD1988_6STACK:
3050 case AD1988_6STACK_DIG:
3051 spec->multiout.max_channels = 8;
3052 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003053 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003054 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3055 else
3056 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003057 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003058 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003059 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003060 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3061 else
3062 spec->mixers[0] = ad1988_6stack_mixers1;
3063 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003064 spec->num_init_verbs = 1;
3065 spec->init_verbs[0] = ad1988_6stack_init_verbs;
3066 if (board_config == AD1988_6STACK_DIG) {
3067 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3068 spec->dig_in_nid = AD1988_SPDIF_IN;
3069 }
3070 break;
3071 case AD1988_3STACK:
3072 case AD1988_3STACK_DIG:
3073 spec->multiout.max_channels = 6;
3074 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003075 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003076 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3077 else
3078 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003079 spec->input_mux = &ad1988_6stack_capture_source;
3080 spec->channel_mode = ad1988_3stack_modes;
3081 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003082 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003083 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003084 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3085 else
3086 spec->mixers[0] = ad1988_3stack_mixers1;
3087 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003088 spec->num_init_verbs = 1;
3089 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3090 if (board_config == AD1988_3STACK_DIG)
3091 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3092 break;
3093 case AD1988_LAPTOP:
3094 case AD1988_LAPTOP_DIG:
3095 spec->multiout.max_channels = 2;
3096 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003097 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003098 spec->input_mux = &ad1988_laptop_capture_source;
3099 spec->num_mixers = 1;
3100 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01003101 spec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003102 spec->num_init_verbs = 1;
3103 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3104 if (board_config == AD1988_LAPTOP_DIG)
3105 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3106 break;
3107 }
3108
Takashi Iwaid32410b12005-11-24 16:06:23 +01003109 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3110 spec->adc_nids = ad1988_adc_nids;
3111 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003112 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3113 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3114 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003115 if (codec->vendor_id >= 0x11d4989a) {
3116 spec->mixers[spec->num_mixers++] =
3117 ad1989_spdif_out_mixers;
3118 spec->init_verbs[spec->num_init_verbs++] =
3119 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003120 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003121 } else {
3122 spec->mixers[spec->num_mixers++] =
3123 ad1988_spdif_out_mixers;
3124 spec->init_verbs[spec->num_init_verbs++] =
3125 ad1988_spdif_init_verbs;
3126 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003127 }
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003128 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003129 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
3130
3131 codec->patch_ops = ad198x_patch_ops;
3132 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003133 case AD1988_AUTO:
3134 codec->patch_ops.init = ad1988_auto_init;
3135 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003136 case AD1988_LAPTOP:
3137 case AD1988_LAPTOP_DIG:
3138 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3139 break;
3140 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003141#ifdef CONFIG_SND_HDA_POWER_SAVE
3142 spec->loopback.amplist = ad1988_loopbacks;
3143#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003144 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003145
3146 return 0;
3147}
3148
3149
3150/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003151 * AD1884 / AD1984
3152 *
3153 * port-B - front line/mic-in
3154 * port-E - aux in/out
3155 * port-F - aux in/out
3156 * port-C - rear line/mic-in
3157 * port-D - rear line/hp-out
3158 * port-A - front line/hp-out
3159 *
3160 * AD1984 = AD1884 + two digital mic-ins
3161 *
3162 * FIXME:
3163 * For simplicity, we share the single DAC for both HP and line-outs
3164 * right now. The inidividual playbacks could be easily implemented,
3165 * but no build-up framework is given, so far.
3166 */
3167
3168static hda_nid_t ad1884_dac_nids[1] = {
3169 0x04,
3170};
3171
3172static hda_nid_t ad1884_adc_nids[2] = {
3173 0x08, 0x09,
3174};
3175
3176static hda_nid_t ad1884_capsrc_nids[2] = {
3177 0x0c, 0x0d,
3178};
3179
3180#define AD1884_SPDIF_OUT 0x02
3181
3182static struct hda_input_mux ad1884_capture_source = {
3183 .num_items = 4,
3184 .items = {
3185 { "Front Mic", 0x0 },
3186 { "Mic", 0x1 },
3187 { "CD", 0x2 },
3188 { "Mix", 0x3 },
3189 },
3190};
3191
3192static struct snd_kcontrol_new ad1884_base_mixers[] = {
3193 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3194 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3195 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3196 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3197 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3198 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3199 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3200 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3201 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3202 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3203 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3204 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003205 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
3206 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3207 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3208 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3209 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3210 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3211 {
3212 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3213 /* The multiple "Capture Source" controls confuse alsamixer
3214 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003215 */
3216 /* .name = "Capture Source", */
3217 .name = "Input Source",
3218 .count = 2,
3219 .info = ad198x_mux_enum_info,
3220 .get = ad198x_mux_enum_get,
3221 .put = ad198x_mux_enum_put,
3222 },
3223 /* SPDIF controls */
3224 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3225 {
3226 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3227 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3228 /* identical with ad1983 */
3229 .info = ad1983_spdif_route_info,
3230 .get = ad1983_spdif_route_get,
3231 .put = ad1983_spdif_route_put,
3232 },
3233 { } /* end */
3234};
3235
3236static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3237 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3238 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3239 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003240 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003241 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003242 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003243 { } /* end */
3244};
3245
3246/*
3247 * initialization verbs
3248 */
3249static struct hda_verb ad1884_init_verbs[] = {
3250 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003251 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3252 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003253 /* Port-A (HP) mixer */
3254 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3255 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3256 /* Port-A pin */
3257 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3258 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3259 /* HP selector - select DAC2 */
3260 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3261 /* Port-D (Line-out) mixer */
3262 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3263 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3264 /* Port-D pin */
3265 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3266 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3267 /* Mono-out mixer */
3268 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3269 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3270 /* Mono-out pin */
3271 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3272 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3273 /* Mono selector */
3274 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3275 /* Port-B (front mic) pin */
3276 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003277 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003278 /* Port-C (rear mic) pin */
3279 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003280 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003281 /* Analog mixer; mute as default */
3282 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3283 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3284 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3285 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3286 /* Analog Mix output amp */
3287 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3288 /* SPDIF output selector */
3289 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3290 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3291 { } /* end */
3292};
3293
Takashi Iwaicb53c622007-08-10 17:21:45 +02003294#ifdef CONFIG_SND_HDA_POWER_SAVE
3295static struct hda_amp_list ad1884_loopbacks[] = {
3296 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3297 { 0x20, HDA_INPUT, 1 }, /* Mic */
3298 { 0x20, HDA_INPUT, 2 }, /* CD */
3299 { 0x20, HDA_INPUT, 4 }, /* Docking */
3300 { } /* end */
3301};
3302#endif
3303
Takashi Iwai2134ea42008-01-10 16:53:55 +01003304static const char *ad1884_slave_vols[] = {
3305 "PCM Playback Volume",
3306 "Mic Playback Volume",
3307 "Mono Playback Volume",
3308 "Front Mic Playback Volume",
3309 "Mic Playback Volume",
3310 "CD Playback Volume",
3311 "Internal Mic Playback Volume",
Akinobu Mitabca68462009-04-06 18:42:42 +09003312 "Docking Mic Playback Volume",
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003313 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003314 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003315 NULL
3316};
3317
Takashi Iwai2bac6472007-05-18 18:21:41 +02003318static int patch_ad1884(struct hda_codec *codec)
3319{
3320 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003321 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003322
3323 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3324 if (spec == NULL)
3325 return -ENOMEM;
3326
Takashi Iwai2bac6472007-05-18 18:21:41 +02003327 codec->spec = spec;
3328
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003329 err = snd_hda_attach_beep_device(codec, 0x10);
3330 if (err < 0) {
3331 ad198x_free(codec);
3332 return err;
3333 }
3334 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3335
Takashi Iwai2bac6472007-05-18 18:21:41 +02003336 spec->multiout.max_channels = 2;
3337 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3338 spec->multiout.dac_nids = ad1884_dac_nids;
3339 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3340 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3341 spec->adc_nids = ad1884_adc_nids;
3342 spec->capsrc_nids = ad1884_capsrc_nids;
3343 spec->input_mux = &ad1884_capture_source;
3344 spec->num_mixers = 1;
3345 spec->mixers[0] = ad1884_base_mixers;
3346 spec->num_init_verbs = 1;
3347 spec->init_verbs[0] = ad1884_init_verbs;
3348 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003349#ifdef CONFIG_SND_HDA_POWER_SAVE
3350 spec->loopback.amplist = ad1884_loopbacks;
3351#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003352 spec->vmaster_nid = 0x04;
3353 /* we need to cover all playback volumes */
3354 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003355
3356 codec->patch_ops = ad198x_patch_ops;
3357
3358 return 0;
3359}
3360
3361/*
3362 * Lenovo Thinkpad T61/X61
3363 */
3364static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003365 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003366 .items = {
3367 { "Mic", 0x0 },
3368 { "Internal Mic", 0x1 },
3369 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003370 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003371 },
3372};
3373
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003374
3375/*
3376 * Dell Precision T3400
3377 */
3378static struct hda_input_mux ad1984_dell_desktop_capture_source = {
3379 .num_items = 3,
3380 .items = {
3381 { "Front Mic", 0x0 },
3382 { "Line-In", 0x1 },
3383 { "Mix", 0x3 },
3384 },
3385};
3386
3387
Takashi Iwai2bac6472007-05-18 18:21:41 +02003388static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3389 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3390 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3391 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3392 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3393 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3394 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3395 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3396 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3397 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3398 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003399 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3400 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003401 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003402 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3403 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3404 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3405 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3406 {
3407 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3408 /* The multiple "Capture Source" controls confuse alsamixer
3409 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003410 */
3411 /* .name = "Capture Source", */
3412 .name = "Input Source",
3413 .count = 2,
3414 .info = ad198x_mux_enum_info,
3415 .get = ad198x_mux_enum_get,
3416 .put = ad198x_mux_enum_put,
3417 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003418 /* SPDIF controls */
3419 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3420 {
3421 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3422 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3423 /* identical with ad1983 */
3424 .info = ad1983_spdif_route_info,
3425 .get = ad1983_spdif_route_get,
3426 .put = ad1983_spdif_route_put,
3427 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003428 { } /* end */
3429};
3430
3431/* additional verbs */
3432static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3433 /* Port-E (docking station mic) pin */
3434 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3435 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3436 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003437 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003438 /* Analog mixer - docking mic; mute as default */
3439 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003440 /* enable EAPD bit */
3441 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003442 { } /* end */
3443};
3444
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003445/*
3446 * Dell Precision T3400
3447 */
3448static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
3449 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3450 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3451 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3452 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3453 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3454 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3455 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3456 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3457 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003458 HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
3459 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3460 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3461 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3462 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3463 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3464 {
3465 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3466 /* The multiple "Capture Source" controls confuse alsamixer
3467 * So call somewhat different..
3468 */
3469 /* .name = "Capture Source", */
3470 .name = "Input Source",
3471 .count = 2,
3472 .info = ad198x_mux_enum_info,
3473 .get = ad198x_mux_enum_get,
3474 .put = ad198x_mux_enum_put,
3475 },
3476 { } /* end */
3477};
3478
Takashi Iwai2bac6472007-05-18 18:21:41 +02003479/* Digial MIC ADC NID 0x05 + 0x06 */
3480static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3481 struct hda_codec *codec,
3482 unsigned int stream_tag,
3483 unsigned int format,
3484 struct snd_pcm_substream *substream)
3485{
3486 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3487 stream_tag, 0, format);
3488 return 0;
3489}
3490
3491static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3492 struct hda_codec *codec,
3493 struct snd_pcm_substream *substream)
3494{
Takashi Iwai888afa12008-03-18 09:57:50 +01003495 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003496 return 0;
3497}
3498
3499static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3500 .substreams = 2,
3501 .channels_min = 2,
3502 .channels_max = 2,
3503 .nid = 0x05,
3504 .ops = {
3505 .prepare = ad1984_pcm_dmic_prepare,
3506 .cleanup = ad1984_pcm_dmic_cleanup
3507 },
3508};
3509
3510static int ad1984_build_pcms(struct hda_codec *codec)
3511{
3512 struct ad198x_spec *spec = codec->spec;
3513 struct hda_pcm *info;
3514 int err;
3515
3516 err = ad198x_build_pcms(codec);
3517 if (err < 0)
3518 return err;
3519
3520 info = spec->pcm_rec + codec->num_pcms;
3521 codec->num_pcms++;
3522 info->name = "AD1984 Digital Mic";
3523 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3524 return 0;
3525}
3526
3527/* models */
3528enum {
3529 AD1984_BASIC,
3530 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003531 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003532 AD1984_MODELS
3533};
3534
3535static const char *ad1984_models[AD1984_MODELS] = {
3536 [AD1984_BASIC] = "basic",
3537 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003538 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003539};
3540
3541static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3542 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003543 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003544 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003545 {}
3546};
3547
3548static int patch_ad1984(struct hda_codec *codec)
3549{
3550 struct ad198x_spec *spec;
3551 int board_config, err;
3552
3553 err = patch_ad1884(codec);
3554 if (err < 0)
3555 return err;
3556 spec = codec->spec;
3557 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3558 ad1984_models, ad1984_cfg_tbl);
3559 switch (board_config) {
3560 case AD1984_BASIC:
3561 /* additional digital mics */
3562 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3563 codec->patch_ops.build_pcms = ad1984_build_pcms;
3564 break;
3565 case AD1984_THINKPAD:
Jerone Youngebf00c52008-01-07 12:22:18 +01003566 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003567 spec->input_mux = &ad1984_thinkpad_capture_source;
3568 spec->mixers[0] = ad1984_thinkpad_mixers;
3569 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
3570 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003571 case AD1984_DELL_DESKTOP:
3572 spec->multiout.dig_out_nid = 0;
3573 spec->input_mux = &ad1984_dell_desktop_capture_source;
3574 spec->mixers[0] = ad1984_dell_desktop_mixers;
3575 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003576 }
3577 return 0;
3578}
3579
3580
3581/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003582 * AD1883 / AD1884A / AD1984A / AD1984B
3583 *
3584 * port-B (0x14) - front mic-in
3585 * port-E (0x1c) - rear mic-in
3586 * port-F (0x16) - CD / ext out
3587 * port-C (0x15) - rear line-in
3588 * port-D (0x12) - rear line-out
3589 * port-A (0x11) - front hp-out
3590 *
3591 * AD1984A = AD1884A + digital-mic
3592 * AD1883 = equivalent with AD1984A
3593 * AD1984B = AD1984A + extra SPDIF-out
3594 *
3595 * FIXME:
3596 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3597 */
3598
3599static hda_nid_t ad1884a_dac_nids[1] = {
3600 0x03,
3601};
3602
3603#define ad1884a_adc_nids ad1884_adc_nids
3604#define ad1884a_capsrc_nids ad1884_capsrc_nids
3605
3606#define AD1884A_SPDIF_OUT 0x02
3607
3608static struct hda_input_mux ad1884a_capture_source = {
3609 .num_items = 5,
3610 .items = {
3611 { "Front Mic", 0x0 },
3612 { "Mic", 0x4 },
3613 { "Line", 0x1 },
3614 { "CD", 0x2 },
3615 { "Mix", 0x3 },
3616 },
3617};
3618
3619static struct snd_kcontrol_new ad1884a_base_mixers[] = {
3620 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3621 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3622 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3623 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3624 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3625 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3626 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3627 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3628 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3629 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3630 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3631 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3632 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3633 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3634 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3635 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003636 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3637 HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
3638 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3639 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3640 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3641 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3642 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3643 {
3644 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3645 /* The multiple "Capture Source" controls confuse alsamixer
3646 * So call somewhat different..
3647 */
3648 /* .name = "Capture Source", */
3649 .name = "Input Source",
3650 .count = 2,
3651 .info = ad198x_mux_enum_info,
3652 .get = ad198x_mux_enum_get,
3653 .put = ad198x_mux_enum_put,
3654 },
3655 /* SPDIF controls */
3656 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3657 {
3658 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3659 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3660 /* identical with ad1983 */
3661 .info = ad1983_spdif_route_info,
3662 .get = ad1983_spdif_route_get,
3663 .put = ad1983_spdif_route_put,
3664 },
3665 { } /* end */
3666};
3667
3668/*
3669 * initialization verbs
3670 */
3671static struct hda_verb ad1884a_init_verbs[] = {
3672 /* DACs; unmute as default */
3673 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3674 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3675 /* Port-A (HP) mixer - route only from analog mixer */
3676 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3677 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3678 /* Port-A pin */
3679 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3680 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3681 /* Port-D (Line-out) mixer - route only from analog mixer */
3682 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3683 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3684 /* Port-D pin */
3685 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3686 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3687 /* Mono-out mixer - route only from analog mixer */
3688 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3689 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3690 /* Mono-out pin */
3691 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3692 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3693 /* Port-B (front mic) pin */
3694 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003695 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003696 /* Port-C (rear line-in) pin */
3697 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003698 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003699 /* Port-E (rear mic) pin */
3700 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3701 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3702 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
3703 /* Port-F (CD) pin */
3704 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3705 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3706 /* Analog mixer; mute as default */
3707 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3708 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3709 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3710 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3711 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
3712 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3713 /* Analog Mix output amp */
3714 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3715 /* capture sources */
3716 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
3717 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3718 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3719 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3720 /* SPDIF output amp */
3721 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3722 { } /* end */
3723};
3724
3725#ifdef CONFIG_SND_HDA_POWER_SAVE
3726static struct hda_amp_list ad1884a_loopbacks[] = {
3727 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3728 { 0x20, HDA_INPUT, 1 }, /* Mic */
3729 { 0x20, HDA_INPUT, 2 }, /* CD */
3730 { 0x20, HDA_INPUT, 4 }, /* Docking */
3731 { } /* end */
3732};
3733#endif
3734
3735/*
3736 * Laptop model
3737 *
3738 * Port A: Headphone jack
3739 * Port B: MIC jack
3740 * Port C: Internal MIC
3741 * Port D: Dock Line Out (if enabled)
3742 * Port E: Dock Line In (if enabled)
3743 * Port F: Internal speakers
3744 */
3745
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003746static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
3747 struct snd_ctl_elem_value *ucontrol)
3748{
3749 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3750 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
3751 int mute = (!ucontrol->value.integer.value[0] &&
3752 !ucontrol->value.integer.value[1]);
3753 /* toggle GPIO1 according to the mute state */
3754 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
3755 mute ? 0x02 : 0x0);
3756 return ret;
3757}
Takashi Iwaic5059252008-02-16 09:43:56 +01003758
3759static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
3760 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003761 {
3762 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3763 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003764 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003765 .info = snd_hda_mixer_amp_switch_info,
3766 .get = snd_hda_mixer_amp_switch_get,
3767 .put = ad1884a_mobile_master_sw_put,
3768 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3769 },
Takashi Iwaic5059252008-02-16 09:43:56 +01003770 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3771 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3772 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3773 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3774 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3775 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3776 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3777 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3778 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003779 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3780 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
3781 HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3782 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3783 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003784 { } /* end */
3785};
3786
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003787static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
3788 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db17e2009-07-02 16:10:23 +02003789 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
3790 {
3791 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3792 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003793 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai099db17e2009-07-02 16:10:23 +02003794 .info = snd_hda_mixer_amp_switch_info,
3795 .get = snd_hda_mixer_amp_switch_get,
3796 .put = ad1884a_mobile_master_sw_put,
3797 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3798 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003799 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3800 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02003801 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
3802 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003803 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3804 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003805 { } /* end */
3806};
3807
Takashi Iwaic5059252008-02-16 09:43:56 +01003808/* mute internal speaker if HP is plugged */
3809static void ad1884a_hp_automute(struct hda_codec *codec)
3810{
3811 unsigned int present;
3812
Takashi Iwaid56757a2009-11-18 08:00:14 +01003813 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01003814 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3815 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3816 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3817 present ? 0x00 : 0x02);
3818}
3819
Takashi Iwai269ef192008-05-30 15:32:15 +02003820/* switch to external mic if plugged */
3821static void ad1884a_hp_automic(struct hda_codec *codec)
3822{
3823 unsigned int present;
3824
Takashi Iwaid56757a2009-11-18 08:00:14 +01003825 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02003826 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
3827 present ? 0 : 1);
3828}
3829
Takashi Iwaic5059252008-02-16 09:43:56 +01003830#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02003831#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01003832
3833/* unsolicited event for HP jack sensing */
3834static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
3835{
Takashi Iwai269ef192008-05-30 15:32:15 +02003836 switch (res >> 26) {
3837 case AD1884A_HP_EVENT:
3838 ad1884a_hp_automute(codec);
3839 break;
3840 case AD1884A_MIC_EVENT:
3841 ad1884a_hp_automic(codec);
3842 break;
3843 }
Takashi Iwaic5059252008-02-16 09:43:56 +01003844}
3845
3846/* initialize jack-sensing, too */
3847static int ad1884a_hp_init(struct hda_codec *codec)
3848{
3849 ad198x_init(codec);
3850 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02003851 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01003852 return 0;
3853}
3854
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003855/* mute internal speaker if HP or docking HP is plugged */
3856static void ad1884a_laptop_automute(struct hda_codec *codec)
3857{
3858 unsigned int present;
3859
Takashi Iwaid56757a2009-11-18 08:00:14 +01003860 present = snd_hda_jack_detect(codec, 0x11);
3861 if (!present)
3862 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003863 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3864 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3865 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3866 present ? 0x00 : 0x02);
3867}
3868
3869/* switch to external mic if plugged */
3870static void ad1884a_laptop_automic(struct hda_codec *codec)
3871{
3872 unsigned int idx;
3873
Takashi Iwaid56757a2009-11-18 08:00:14 +01003874 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003875 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01003876 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003877 idx = 4;
3878 else
3879 idx = 1;
3880 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
3881}
3882
3883/* unsolicited event for HP jack sensing */
3884static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
3885 unsigned int res)
3886{
3887 switch (res >> 26) {
3888 case AD1884A_HP_EVENT:
3889 ad1884a_laptop_automute(codec);
3890 break;
3891 case AD1884A_MIC_EVENT:
3892 ad1884a_laptop_automic(codec);
3893 break;
3894 }
3895}
3896
3897/* initialize jack-sensing, too */
3898static int ad1884a_laptop_init(struct hda_codec *codec)
3899{
3900 ad198x_init(codec);
3901 ad1884a_laptop_automute(codec);
3902 ad1884a_laptop_automic(codec);
3903 return 0;
3904}
3905
Takashi Iwaic5059252008-02-16 09:43:56 +01003906/* additional verbs for laptop model */
3907static struct hda_verb ad1884a_laptop_verbs[] = {
3908 /* Port-A (HP) pin - always unmuted */
3909 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3910 /* Port-F (int speaker) mixer - route only from analog mixer */
3911 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3912 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08003913 /* Port-F (int speaker) pin */
3914 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01003915 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08003916 /* required for compaq 6530s/6531s speaker output */
3917 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003918 /* Port-C pin - internal mic-in */
3919 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3920 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3921 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02003922 /* Port-D (docking line-out) pin - default unmuted */
3923 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01003924 /* analog mix */
3925 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3926 /* unsolicited event for pin-sense */
3927 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003928 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003929 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003930 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02003931 /* allow to touch GPIO1 (for mute control) */
3932 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
3933 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
3934 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01003935 { } /* end */
3936};
3937
Takashi Iwai73156132009-04-23 08:24:48 +02003938static struct hda_verb ad1884a_mobile_verbs[] = {
3939 /* DACs; unmute as default */
3940 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3941 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3942 /* Port-A (HP) mixer - route only from analog mixer */
3943 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3944 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3945 /* Port-A pin */
3946 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3947 /* Port-A (HP) pin - always unmuted */
3948 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3949 /* Port-B (mic jack) pin */
3950 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3951 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3952 /* Port-C (int mic) pin */
3953 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3954 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3955 /* Port-F (int speaker) mixer - route only from analog mixer */
3956 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3957 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3958 /* Port-F pin */
3959 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3960 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3961 /* Analog mixer; mute as default */
3962 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3963 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3964 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3965 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3966 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3967 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3968 /* Analog Mix output amp */
3969 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3970 /* capture sources */
3971 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
3972 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3973 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3974 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3975 /* unsolicited event for pin-sense */
3976 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
3977 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db17e2009-07-02 16:10:23 +02003978 /* allow to touch GPIO1 (for mute control) */
3979 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
3980 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
3981 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02003982 { } /* end */
3983};
3984
Takashi Iwaic5059252008-02-16 09:43:56 +01003985/*
Takashi Iwaif0813742008-03-18 12:13:03 +01003986 * Thinkpad X300
3987 * 0x11 - HP
3988 * 0x12 - speaker
3989 * 0x14 - mic-in
3990 * 0x17 - built-in mic
3991 */
3992
3993static struct hda_verb ad1984a_thinkpad_verbs[] = {
3994 /* HP unmute */
3995 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3996 /* analog mix */
3997 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3998 /* turn on EAPD */
3999 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4000 /* unsolicited event for pin-sense */
4001 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4002 /* internal mic - dmic */
4003 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02004004 /* set magic COEFs for dmic */
4005 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4006 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01004007 { } /* end */
4008};
4009
4010static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
4011 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4012 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4013 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4014 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4015 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4016 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01004017 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
4018 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4019 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4020 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4021 {
4022 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4023 .name = "Capture Source",
4024 .info = ad198x_mux_enum_info,
4025 .get = ad198x_mux_enum_get,
4026 .put = ad198x_mux_enum_put,
4027 },
4028 { } /* end */
4029};
4030
4031static struct hda_input_mux ad1984a_thinkpad_capture_source = {
4032 .num_items = 3,
4033 .items = {
4034 { "Mic", 0x0 },
4035 { "Internal Mic", 0x5 },
4036 { "Mix", 0x3 },
4037 },
4038};
4039
4040/* mute internal speaker if HP is plugged */
4041static void ad1984a_thinkpad_automute(struct hda_codec *codec)
4042{
4043 unsigned int present;
4044
Takashi Iwaid56757a2009-11-18 08:00:14 +01004045 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01004046 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
4047 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4048}
4049
4050/* unsolicited event for HP jack sensing */
4051static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
4052 unsigned int res)
4053{
4054 if ((res >> 26) != AD1884A_HP_EVENT)
4055 return;
4056 ad1984a_thinkpad_automute(codec);
4057}
4058
4059/* initialize jack-sensing, too */
4060static int ad1984a_thinkpad_init(struct hda_codec *codec)
4061{
4062 ad198x_init(codec);
4063 ad1984a_thinkpad_automute(codec);
4064 return 0;
4065}
4066
4067/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004068 * HP Touchsmart
4069 * port-A (0x11) - front hp-out
4070 * port-B (0x14) - unused
4071 * port-C (0x15) - unused
4072 * port-D (0x12) - rear line out
4073 * port-E (0x1c) - front mic-in
4074 * port-F (0x16) - Internal speakers
4075 * digital-mic (0x17) - Internal mic
4076 */
4077
4078static struct hda_verb ad1984a_touchsmart_verbs[] = {
4079 /* DACs; unmute as default */
4080 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4081 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4082 /* Port-A (HP) mixer - route only from analog mixer */
4083 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4084 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4085 /* Port-A pin */
4086 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4087 /* Port-A (HP) pin - always unmuted */
4088 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4089 /* Port-E (int speaker) mixer - route only from analog mixer */
4090 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4091 /* Port-E pin */
4092 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4093 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4094 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4095 /* Port-F (int speaker) mixer - route only from analog mixer */
4096 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4097 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4098 /* Port-F pin */
4099 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4100 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4101 /* Analog mixer; mute as default */
4102 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4103 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4104 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4105 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4106 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4107 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4108 /* Analog Mix output amp */
4109 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4110 /* capture sources */
4111 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4112 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4113 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4114 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4115 /* unsolicited event for pin-sense */
4116 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4117 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4118 /* allow to touch GPIO1 (for mute control) */
4119 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4120 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4121 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4122 /* internal mic - dmic */
4123 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4124 /* set magic COEFs for dmic */
4125 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4126 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4127 { } /* end */
4128};
4129
4130static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
4131 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4132/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4133 {
4134 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004135 .subdevice = HDA_SUBDEV_AMP_FLAG,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004136 .name = "Master Playback Switch",
4137 .info = snd_hda_mixer_amp_switch_info,
4138 .get = snd_hda_mixer_amp_switch_get,
4139 .put = ad1884a_mobile_master_sw_put,
4140 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4141 },
4142 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4143 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4144 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4145 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4146 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
4147 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4148 { } /* end */
4149};
4150
4151/* switch to external mic if plugged */
4152static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4153{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004154 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004155 snd_hda_codec_write(codec, 0x0c, 0,
4156 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004157 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004158 snd_hda_codec_write(codec, 0x0c, 0,
4159 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004160}
4161
4162
4163/* unsolicited event for HP jack sensing */
4164static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4165 unsigned int res)
4166{
4167 switch (res >> 26) {
4168 case AD1884A_HP_EVENT:
4169 ad1884a_hp_automute(codec);
4170 break;
4171 case AD1884A_MIC_EVENT:
4172 ad1984a_touchsmart_automic(codec);
4173 break;
4174 }
4175}
4176
4177/* initialize jack-sensing, too */
4178static int ad1984a_touchsmart_init(struct hda_codec *codec)
4179{
4180 ad198x_init(codec);
4181 ad1884a_hp_automute(codec);
4182 ad1984a_touchsmart_automic(codec);
4183 return 0;
4184}
4185
4186
4187/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004188 */
4189
4190enum {
4191 AD1884A_DESKTOP,
4192 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004193 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004194 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004195 AD1984A_TOUCHSMART,
Takashi Iwaic5059252008-02-16 09:43:56 +01004196 AD1884A_MODELS
4197};
4198
4199static const char *ad1884a_models[AD1884A_MODELS] = {
4200 [AD1884A_DESKTOP] = "desktop",
4201 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004202 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004203 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004204 [AD1984A_TOUCHSMART] = "touchsmart",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004205};
4206
4207static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
4208 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004209 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004210 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004211 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004212 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004213 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4214 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004215 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004216 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004217 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004218 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004219};
4220
4221static int patch_ad1884a(struct hda_codec *codec)
4222{
4223 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004224 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004225
4226 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4227 if (spec == NULL)
4228 return -ENOMEM;
4229
Takashi Iwaic5059252008-02-16 09:43:56 +01004230 codec->spec = spec;
4231
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004232 err = snd_hda_attach_beep_device(codec, 0x10);
4233 if (err < 0) {
4234 ad198x_free(codec);
4235 return err;
4236 }
4237 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4238
Takashi Iwaic5059252008-02-16 09:43:56 +01004239 spec->multiout.max_channels = 2;
4240 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4241 spec->multiout.dac_nids = ad1884a_dac_nids;
4242 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4243 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4244 spec->adc_nids = ad1884a_adc_nids;
4245 spec->capsrc_nids = ad1884a_capsrc_nids;
4246 spec->input_mux = &ad1884a_capture_source;
4247 spec->num_mixers = 1;
4248 spec->mixers[0] = ad1884a_base_mixers;
4249 spec->num_init_verbs = 1;
4250 spec->init_verbs[0] = ad1884a_init_verbs;
4251 spec->spdif_route = 0;
4252#ifdef CONFIG_SND_HDA_POWER_SAVE
4253 spec->loopback.amplist = ad1884a_loopbacks;
4254#endif
4255 codec->patch_ops = ad198x_patch_ops;
4256
4257 /* override some parameters */
4258 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004259 ad1884a_models,
4260 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01004261 switch (board_config) {
4262 case AD1884A_LAPTOP:
4263 spec->mixers[0] = ad1884a_laptop_mixers;
4264 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4265 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004266 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4267 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004268 /* set the upper-limit for mixer amp to 0dB for avoiding the
4269 * possible damage by overloading
4270 */
4271 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4272 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4273 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4274 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4275 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004276 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004277 case AD1884A_MOBILE:
4278 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004279 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004280 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004281 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4282 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004283 /* set the upper-limit for mixer amp to 0dB for avoiding the
4284 * possible damage by overloading
4285 */
4286 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4287 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4288 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4289 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4290 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004291 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004292 case AD1884A_THINKPAD:
4293 spec->mixers[0] = ad1984a_thinkpad_mixers;
4294 spec->init_verbs[spec->num_init_verbs++] =
4295 ad1984a_thinkpad_verbs;
4296 spec->multiout.dig_out_nid = 0;
4297 spec->input_mux = &ad1984a_thinkpad_capture_source;
4298 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4299 codec->patch_ops.init = ad1984a_thinkpad_init;
4300 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004301 case AD1984A_TOUCHSMART:
4302 spec->mixers[0] = ad1984a_touchsmart_mixers;
4303 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4304 spec->multiout.dig_out_nid = 0;
4305 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4306 codec->patch_ops.init = ad1984a_touchsmart_init;
4307 /* set the upper-limit for mixer amp to 0dB for avoiding the
4308 * possible damage by overloading
4309 */
4310 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4311 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4312 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4313 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4314 (1 << AC_AMPCAP_MUTE_SHIFT));
4315 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004316 }
4317
4318 return 0;
4319}
4320
4321
4322/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004323 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004324 *
4325 * port-A - front hp-out
4326 * port-B - front mic-in
4327 * port-C - rear line-in, shared surr-out (3stack)
4328 * port-D - rear line-out
4329 * port-E - rear mic-in, shared clfe-out (3stack)
4330 * port-F - rear surr-out (6stack)
4331 * port-G - rear clfe-out (6stack)
4332 */
4333
4334static hda_nid_t ad1882_dac_nids[3] = {
4335 0x04, 0x03, 0x05
4336};
4337
4338static hda_nid_t ad1882_adc_nids[2] = {
4339 0x08, 0x09,
4340};
4341
4342static hda_nid_t ad1882_capsrc_nids[2] = {
4343 0x0c, 0x0d,
4344};
4345
4346#define AD1882_SPDIF_OUT 0x02
4347
4348/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
4349static struct hda_input_mux ad1882_capture_source = {
4350 .num_items = 5,
4351 .items = {
4352 { "Front Mic", 0x1 },
4353 { "Mic", 0x4 },
4354 { "Line", 0x2 },
4355 { "CD", 0x3 },
4356 { "Mix", 0x7 },
4357 },
4358};
4359
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004360/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
4361static struct hda_input_mux ad1882a_capture_source = {
4362 .num_items = 5,
4363 .items = {
4364 { "Front Mic", 0x1 },
4365 { "Mic", 0x4},
4366 { "Line", 0x2 },
4367 { "Digital Mic", 0x06 },
4368 { "Mix", 0x7 },
4369 },
4370};
4371
Takashi Iwai0ac85512007-06-20 15:46:13 +02004372static struct snd_kcontrol_new ad1882_base_mixers[] = {
4373 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4374 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4375 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4376 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4377 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4378 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4379 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4380 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004381
Takashi Iwai0ac85512007-06-20 15:46:13 +02004382 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
4383 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
4384 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
4385 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4386 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4387 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4388 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4389 {
4390 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4391 /* The multiple "Capture Source" controls confuse alsamixer
4392 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004393 */
4394 /* .name = "Capture Source", */
4395 .name = "Input Source",
4396 .count = 2,
4397 .info = ad198x_mux_enum_info,
4398 .get = ad198x_mux_enum_get,
4399 .put = ad198x_mux_enum_put,
4400 },
4401 /* SPDIF controls */
4402 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4403 {
4404 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4405 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4406 /* identical with ad1983 */
4407 .info = ad1983_spdif_route_info,
4408 .get = ad1983_spdif_route_get,
4409 .put = ad1983_spdif_route_put,
4410 },
4411 { } /* end */
4412};
4413
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004414static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
4415 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4416 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4417 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4418 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4419 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4420 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4421 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4422 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004423 { } /* end */
4424};
4425
4426static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
4427 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4428 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4429 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4430 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4431 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4432 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4433 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4434 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004435 HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
4436 { } /* end */
4437};
4438
Takashi Iwai0ac85512007-06-20 15:46:13 +02004439static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
4440 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4441 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4442 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4443 {
4444 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4445 .name = "Channel Mode",
4446 .info = ad198x_ch_mode_info,
4447 .get = ad198x_ch_mode_get,
4448 .put = ad198x_ch_mode_put,
4449 },
4450 { } /* end */
4451};
4452
4453static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
4454 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4455 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4456 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4457 { } /* end */
4458};
4459
4460static struct hda_verb ad1882_ch2_init[] = {
4461 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4462 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4463 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4464 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4465 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4466 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4467 { } /* end */
4468};
4469
4470static struct hda_verb ad1882_ch4_init[] = {
4471 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4472 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4473 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4474 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4475 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4476 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4477 { } /* end */
4478};
4479
4480static struct hda_verb ad1882_ch6_init[] = {
4481 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4482 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4483 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4484 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4485 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4486 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4487 { } /* end */
4488};
4489
4490static struct hda_channel_mode ad1882_modes[3] = {
4491 { 2, ad1882_ch2_init },
4492 { 4, ad1882_ch4_init },
4493 { 6, ad1882_ch6_init },
4494};
4495
4496/*
4497 * initialization verbs
4498 */
4499static struct hda_verb ad1882_init_verbs[] = {
4500 /* DACs; mute as default */
4501 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4502 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4503 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4504 /* Port-A (HP) mixer */
4505 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4506 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4507 /* Port-A pin */
4508 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4509 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4510 /* HP selector - select DAC2 */
4511 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4512 /* Port-D (Line-out) mixer */
4513 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4514 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4515 /* Port-D pin */
4516 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4517 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4518 /* Mono-out mixer */
4519 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4520 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4521 /* Mono-out pin */
4522 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4523 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4524 /* Port-B (front mic) pin */
4525 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4526 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4527 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4528 /* Port-C (line-in) pin */
4529 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4530 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4531 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4532 /* Port-C mixer - mute as input */
4533 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4534 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4535 /* Port-E (mic-in) pin */
4536 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4537 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4538 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4539 /* Port-E mixer - mute as input */
4540 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4541 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4542 /* Port-F (surround) */
4543 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4544 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4545 /* Port-G (CLFE) */
4546 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4547 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4548 /* Analog mixer; mute as default */
4549 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4550 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4551 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4552 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4553 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4554 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4555 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4556 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4557 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4558 /* Analog Mix output amp */
4559 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4560 /* SPDIF output selector */
4561 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4562 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4563 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4564 { } /* end */
4565};
4566
Takashi Iwaicb53c622007-08-10 17:21:45 +02004567#ifdef CONFIG_SND_HDA_POWER_SAVE
4568static struct hda_amp_list ad1882_loopbacks[] = {
4569 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4570 { 0x20, HDA_INPUT, 1 }, /* Mic */
4571 { 0x20, HDA_INPUT, 4 }, /* Line */
4572 { 0x20, HDA_INPUT, 6 }, /* CD */
4573 { } /* end */
4574};
4575#endif
4576
Takashi Iwai0ac85512007-06-20 15:46:13 +02004577/* models */
4578enum {
4579 AD1882_3STACK,
4580 AD1882_6STACK,
4581 AD1882_MODELS
4582};
4583
4584static const char *ad1882_models[AD1986A_MODELS] = {
4585 [AD1882_3STACK] = "3stack",
4586 [AD1882_6STACK] = "6stack",
4587};
4588
4589
4590static int patch_ad1882(struct hda_codec *codec)
4591{
4592 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004593 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004594
4595 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4596 if (spec == NULL)
4597 return -ENOMEM;
4598
Takashi Iwai0ac85512007-06-20 15:46:13 +02004599 codec->spec = spec;
4600
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004601 err = snd_hda_attach_beep_device(codec, 0x10);
4602 if (err < 0) {
4603 ad198x_free(codec);
4604 return err;
4605 }
4606 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4607
Takashi Iwai0ac85512007-06-20 15:46:13 +02004608 spec->multiout.max_channels = 6;
4609 spec->multiout.num_dacs = 3;
4610 spec->multiout.dac_nids = ad1882_dac_nids;
4611 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
4612 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
4613 spec->adc_nids = ad1882_adc_nids;
4614 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004615 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004616 spec->input_mux = &ad1882_capture_source;
4617 else
4618 spec->input_mux = &ad1882a_capture_source;
4619 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004620 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004621 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004622 spec->mixers[1] = ad1882_loopback_mixers;
4623 else
4624 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004625 spec->num_init_verbs = 1;
4626 spec->init_verbs[0] = ad1882_init_verbs;
4627 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02004628#ifdef CONFIG_SND_HDA_POWER_SAVE
4629 spec->loopback.amplist = ad1882_loopbacks;
4630#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01004631 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004632
4633 codec->patch_ops = ad198x_patch_ops;
4634
4635 /* override some parameters */
4636 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
4637 ad1882_models, NULL);
4638 switch (board_config) {
4639 default:
4640 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004641 spec->num_mixers = 3;
4642 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004643 spec->channel_mode = ad1882_modes;
4644 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
4645 spec->need_dac_fix = 1;
4646 spec->multiout.max_channels = 2;
4647 spec->multiout.num_dacs = 1;
4648 break;
4649 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004650 spec->num_mixers = 3;
4651 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004652 break;
4653 }
4654 return 0;
4655}
4656
4657
4658/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004659 * patch entries
4660 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004661static struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004662 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02004663 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004664 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004665 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004666 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
4667 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02004668 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
4669 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004670 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004671 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01004672 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02004673 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004674 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02004675 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
4676 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004677 {} /* terminator */
4678};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004679
4680MODULE_ALIAS("snd-hda-codec-id:11d4*");
4681
4682MODULE_LICENSE("GPL");
4683MODULE_DESCRIPTION("Analog Devices HD-audio codec");
4684
4685static struct hda_codec_preset_list analog_list = {
4686 .preset = snd_hda_preset_analog,
4687 .owner = THIS_MODULE,
4688};
4689
4690static int __init patch_analog_init(void)
4691{
4692 return snd_hda_add_codec_preset(&analog_list);
4693}
4694
4695static void __exit patch_analog_exit(void)
4696{
4697 snd_hda_delete_codec_preset(&analog_list);
4698}
4699
4700module_init(patch_analog_init)
4701module_exit(patch_analog_exit)