blob: 865715e3f938a84ef21434c9927a17ada6a1c6f5 [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
Daniel T Chenea52bf22009-12-27 18:48:29 -0500444static inline void ad198x_shutup(struct hda_codec *codec)
445{
446 snd_hda_shutup_pins(codec);
447}
448
Takashi Iwai603c4012008-07-30 15:01:44 +0200449static void ad198x_free_kctls(struct hda_codec *codec)
450{
451 struct ad198x_spec *spec = codec->spec;
452
453 if (spec->kctls.list) {
454 struct snd_kcontrol_new *kctl = spec->kctls.list;
455 int i;
456 for (i = 0; i < spec->kctls.used; i++)
457 kfree(kctl[i].name);
458 }
459 snd_array_free(&spec->kctls);
460}
461
Daniel T Chenea52bf22009-12-27 18:48:29 -0500462static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
463 hda_nid_t hp)
464{
465 struct ad198x_spec *spec = codec->spec;
466 snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
467 !spec->inv_eapd ? 0x00 : 0x02);
468 snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
469 !spec->inv_eapd ? 0x00 : 0x02);
470}
471
472static void ad198x_power_eapd(struct hda_codec *codec)
473{
474 /* We currently only handle front, HP */
475 switch (codec->vendor_id) {
476 case 0x11d41882:
477 case 0x11d4882a:
478 case 0x11d41884:
479 case 0x11d41984:
480 case 0x11d41883:
481 case 0x11d4184a:
482 case 0x11d4194a:
483 case 0x11d4194b:
484 ad198x_power_eapd_write(codec, 0x12, 0x11);
485 break;
486 case 0x11d41981:
487 case 0x11d41983:
488 ad198x_power_eapd_write(codec, 0x05, 0x06);
489 break;
490 case 0x11d41986:
491 ad198x_power_eapd_write(codec, 0x1b, 0x1a);
492 break;
493 case 0x11d41988:
494 case 0x11d4198b:
495 case 0x11d4989a:
496 case 0x11d4989b:
497 ad198x_power_eapd_write(codec, 0x29, 0x22);
498 break;
499 }
500}
501
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200502static void ad198x_free(struct hda_codec *codec)
503{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100504 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +0100505
Takashi Iwai603c4012008-07-30 15:01:44 +0200506 if (!spec)
507 return;
508
Daniel T Chenea52bf22009-12-27 18:48:29 -0500509 ad198x_shutup(codec);
Takashi Iwai603c4012008-07-30 15:01:44 +0200510 ad198x_free_kctls(codec);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100511 kfree(spec);
512 snd_hda_detach_beep_device(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200513}
514
Daniel T Chenea52bf22009-12-27 18:48:29 -0500515#ifdef SND_HDA_NEEDS_RESUME
516static int ad198x_suspend(struct hda_codec *codec, pm_message_t state)
517{
518 ad198x_shutup(codec);
519 ad198x_power_eapd(codec);
520 return 0;
521}
522
523static int ad198x_resume(struct hda_codec *codec)
524{
525 ad198x_init(codec);
526 snd_hda_codec_resume_amp(codec);
527 snd_hda_codec_resume_cache(codec);
528 return 0;
529}
530#endif
531
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200532static struct hda_codec_ops ad198x_patch_ops = {
533 .build_controls = ad198x_build_controls,
534 .build_pcms = ad198x_build_pcms,
535 .init = ad198x_init,
536 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200537#ifdef CONFIG_SND_HDA_POWER_SAVE
538 .check_power_status = ad198x_check_power_status,
539#endif
Daniel T Chenea52bf22009-12-27 18:48:29 -0500540#ifdef SND_HDA_NEEDS_RESUME
541 .suspend = ad198x_suspend,
542 .resume = ad198x_resume,
543#endif
544 .reboot_notify = ad198x_shutup,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200545};
546
547
548/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100549 * EAPD control
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100550 * the private value = nid
Takashi Iwai18a815d2006-03-01 19:54:39 +0100551 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200552#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100553
554static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
555 struct snd_ctl_elem_value *ucontrol)
556{
557 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
558 struct ad198x_spec *spec = codec->spec;
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100559 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100560 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
561 else
562 ucontrol->value.integer.value[0] = spec->cur_eapd;
563 return 0;
564}
565
566static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
567 struct snd_ctl_elem_value *ucontrol)
568{
569 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
570 struct ad198x_spec *spec = codec->spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +0100571 hda_nid_t nid = kcontrol->private_value & 0xff;
572 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100573 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100574 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100575 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200576 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100577 return 0;
578 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200579 snd_hda_codec_write_cache(codec, nid,
580 0, AC_VERB_SET_EAPD_BTLENABLE,
581 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100582 return 1;
583}
584
Takashi Iwai9230d212006-03-13 13:49:49 +0100585static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
586 struct snd_ctl_elem_info *uinfo);
587static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
588 struct snd_ctl_elem_value *ucontrol);
589static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
590 struct snd_ctl_elem_value *ucontrol);
591
592
Takashi Iwai18a815d2006-03-01 19:54:39 +0100593/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200594 * AD1986A specific
595 */
596
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597#define AD1986A_SPDIF_OUT 0x02
598#define AD1986A_FRONT_DAC 0x03
599#define AD1986A_SURR_DAC 0x04
600#define AD1986A_CLFE_DAC 0x05
601#define AD1986A_ADC 0x06
602
603static hda_nid_t ad1986a_dac_nids[3] = {
604 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
605};
Takashi Iwai985be542005-11-02 18:26:49 +0100606static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +0100607static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608
609static struct hda_input_mux ad1986a_capture_source = {
610 .num_items = 7,
611 .items = {
612 { "Mic", 0x0 },
613 { "CD", 0x1 },
614 { "Aux", 0x3 },
615 { "Line", 0x4 },
616 { "Mix", 0x5 },
617 { "Mono", 0x6 },
618 { "Phone", 0x7 },
619 },
620};
621
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622
Takashi Iwai532d5382007-07-27 19:02:40 +0200623static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
624 .ops = &snd_hda_bind_vol,
625 .values = {
626 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
627 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
628 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
629 0
630 },
631};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632
Takashi Iwai532d5382007-07-27 19:02:40 +0200633static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
634 .ops = &snd_hda_bind_sw,
635 .values = {
636 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
637 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
638 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
639 0
640 },
641};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
643/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 * mixers
645 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100646static struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200647 /*
648 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
649 */
650 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
651 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
653 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
654 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
655 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
656 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
657 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
658 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
659 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
660 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
661 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
662 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
663 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
664 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
665 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
666 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
667 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
668 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
669 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100670 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
672 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
673 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
674 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
675 {
676 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
677 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200678 .info = ad198x_mux_enum_info,
679 .get = ad198x_mux_enum_get,
680 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 },
682 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
683 { } /* end */
684};
685
Takashi Iwai9230d212006-03-13 13:49:49 +0100686/* additional mixers for 3stack mode */
687static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
688 {
689 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
690 .name = "Channel Mode",
691 .info = ad198x_ch_mode_info,
692 .get = ad198x_ch_mode_get,
693 .put = ad198x_ch_mode_put,
694 },
695 { } /* end */
696};
697
698/* laptop model - 2ch only */
699static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
700
Takashi Iwai20a45e82007-08-15 22:20:45 +0200701/* master controls both pins 0x1a and 0x1b */
702static struct hda_bind_ctls ad1986a_laptop_master_vol = {
703 .ops = &snd_hda_bind_vol,
704 .values = {
705 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
706 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
707 0,
708 },
709};
710
711static struct hda_bind_ctls ad1986a_laptop_master_sw = {
712 .ops = &snd_hda_bind_sw,
713 .values = {
714 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
715 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
716 0,
717 },
718};
719
Takashi Iwai9230d212006-03-13 13:49:49 +0100720static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
721 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
722 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200723 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
724 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100725 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
726 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
727 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
728 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
729 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
730 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
731 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
732 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100733 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100734 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100735 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
736 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
737 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
738 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
739 {
740 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
741 .name = "Capture Source",
742 .info = ad198x_mux_enum_info,
743 .get = ad198x_mux_enum_get,
744 .put = ad198x_mux_enum_put,
745 },
746 { } /* end */
747};
748
Takashi Iwai825aa972006-03-17 10:50:49 +0100749/* laptop-eapd model - 2ch only */
750
Takashi Iwai825aa972006-03-17 10:50:49 +0100751static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
752 .num_items = 3,
753 .items = {
754 { "Mic", 0x0 },
755 { "Internal Mic", 0x4 },
756 { "Mix", 0x5 },
757 },
758};
759
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100760static struct hda_input_mux ad1986a_automic_capture_source = {
761 .num_items = 2,
762 .items = {
763 { "Mic", 0x0 },
764 { "Mix", 0x5 },
765 },
766};
767
Takashi Iwai16d11a82009-06-24 14:07:53 +0200768static struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200769 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
770 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai16d11a82009-06-24 14:07:53 +0200771 { } /* end */
772};
773
774static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai825aa972006-03-17 10:50:49 +0100775 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
776 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100777 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
778 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
779 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
780 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
781 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
782 {
783 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
784 .name = "Capture Source",
785 .info = ad198x_mux_enum_info,
786 .get = ad198x_mux_enum_get,
787 .put = ad198x_mux_enum_put,
788 },
789 {
790 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
791 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100792 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwai1725b822008-11-21 02:25:48 +0100793 .info = ad198x_eapd_info,
794 .get = ad198x_eapd_get,
795 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100796 .private_value = 0x1b, /* port-D */
Takashi Iwai1725b822008-11-21 02:25:48 +0100797 },
798 { } /* end */
799};
800
Takashi Iwai16d11a82009-06-24 14:07:53 +0200801static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
802 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
803 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
Takashi Iwai825aa972006-03-17 10:50:49 +0100804 { } /* end */
805};
806
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100807/* re-connect the mic boost input according to the jack sensing */
808static void ad1986a_automic(struct hda_codec *codec)
809{
810 unsigned int present;
Takashi Iwaid56757a2009-11-18 08:00:14 +0100811 present = snd_hda_jack_detect(codec, 0x1f);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100812 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
813 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
Takashi Iwaid56757a2009-11-18 08:00:14 +0100814 present ? 0 : 2);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100815}
816
817#define AD1986A_MIC_EVENT 0x36
818
819static void ad1986a_automic_unsol_event(struct hda_codec *codec,
820 unsigned int res)
821{
822 if ((res >> 26) != AD1986A_MIC_EVENT)
823 return;
824 ad1986a_automic(codec);
825}
826
827static int ad1986a_automic_init(struct hda_codec *codec)
828{
829 ad198x_init(codec);
830 ad1986a_automic(codec);
831 return 0;
832}
833
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200834/* laptop-automute - 2ch only */
835
836static void ad1986a_update_hp(struct hda_codec *codec)
837{
838 struct ad198x_spec *spec = codec->spec;
839 unsigned int mute;
840
841 if (spec->jack_present)
842 mute = HDA_AMP_MUTE; /* mute internal speaker */
843 else
844 /* unmute internal speaker if necessary */
845 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
846 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
847 HDA_AMP_MUTE, mute);
848}
849
850static void ad1986a_hp_automute(struct hda_codec *codec)
851{
852 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200853
Takashi Iwaid56757a2009-11-18 08:00:14 +0100854 spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
Takashi Iwai03c405a2009-06-24 14:10:15 +0200855 if (spec->inv_jack_detect)
856 spec->jack_present = !spec->jack_present;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200857 ad1986a_update_hp(codec);
858}
859
860#define AD1986A_HP_EVENT 0x37
861
862static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
863{
864 if ((res >> 26) != AD1986A_HP_EVENT)
865 return;
866 ad1986a_hp_automute(codec);
867}
868
869static int ad1986a_hp_init(struct hda_codec *codec)
870{
871 ad198x_init(codec);
872 ad1986a_hp_automute(codec);
873 return 0;
874}
875
876/* bind hp and internal speaker mute (with plug check) */
877static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
878 struct snd_ctl_elem_value *ucontrol)
879{
880 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
881 long *valp = ucontrol->value.integer.value;
882 int change;
883
884 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
885 HDA_AMP_MUTE,
886 valp[0] ? 0 : HDA_AMP_MUTE);
887 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
888 HDA_AMP_MUTE,
889 valp[1] ? 0 : HDA_AMP_MUTE);
890 if (change)
891 ad1986a_update_hp(codec);
892 return change;
893}
894
Takashi Iwai16d11a82009-06-24 14:07:53 +0200895static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200896 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
897 {
898 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
899 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100900 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200901 .info = snd_hda_mixer_amp_switch_info,
902 .get = snd_hda_mixer_amp_switch_get,
903 .put = ad1986a_hp_master_sw_put,
904 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
905 },
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200906 { } /* end */
907};
908
Takashi Iwai16d11a82009-06-24 14:07:53 +0200909
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910/*
911 * initialization verbs
912 */
913static struct hda_verb ad1986a_init_verbs[] = {
914 /* Front, Surround, CLFE DAC; mute as default */
915 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
916 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
917 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
918 /* Downmix - off */
919 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
920 /* HP, Line-Out, Surround, CLFE selectors */
921 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
922 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
923 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
924 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
925 /* Mono selector */
926 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
927 /* Mic selector: Mic 1/2 pin */
928 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
929 /* Line-in selector: Line-in */
930 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
931 /* Mic 1/2 swap */
932 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
933 /* Record selector: mic */
934 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
935 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
936 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
937 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
938 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
939 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
940 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
941 /* PC beep */
942 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
943 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
944 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
945 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
946 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
947 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
948 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200949 /* HP Pin */
950 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
951 /* Front, Surround, CLFE Pins */
952 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
953 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
954 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
955 /* Mono Pin */
956 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
957 /* Mic Pin */
958 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
959 /* Line, Aux, CD, Beep-In Pin */
960 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
961 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
962 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
963 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
964 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 { } /* end */
966};
967
Takashi Iwai9230d212006-03-13 13:49:49 +0100968static struct hda_verb ad1986a_ch2_init[] = {
969 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200970 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
971 /* Line-in selectors */
972 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100973 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200974 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
975 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
976 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100977 { } /* end */
978};
979
980static struct hda_verb ad1986a_ch4_init[] = {
981 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200982 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
983 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100984 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200985 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
986 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100987 { } /* end */
988};
989
990static struct hda_verb ad1986a_ch6_init[] = {
991 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200992 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
993 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100994 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200995 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
996 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100997 { } /* end */
998};
999
1000static struct hda_channel_mode ad1986a_modes[3] = {
1001 { 2, ad1986a_ch2_init },
1002 { 4, ad1986a_ch4_init },
1003 { 6, ad1986a_ch6_init },
1004};
1005
Takashi Iwai825aa972006-03-17 10:50:49 +01001006/* eapd initialization */
1007static struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001008 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa972006-03-17 10:50:49 +01001009 {}
1010};
1011
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001012static struct hda_verb ad1986a_automic_verbs[] = {
1013 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1014 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1015 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
1016 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
1017 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
1018 {}
1019};
1020
Tobin Davisf36090f2007-01-08 11:07:12 +01001021/* Ultra initialization */
1022static struct hda_verb ad1986a_ultra_init[] = {
1023 /* eapd initialization */
1024 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
1025 /* CLFE -> Mic in */
1026 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
1027 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1028 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
1029 { } /* end */
1030};
1031
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001032/* pin sensing on HP jack */
1033static struct hda_verb ad1986a_hp_init_verbs[] = {
1034 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
1035 {}
1036};
1037
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001038static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
1039 unsigned int res)
1040{
1041 switch (res >> 26) {
1042 case AD1986A_HP_EVENT:
1043 ad1986a_hp_automute(codec);
1044 break;
1045 case AD1986A_MIC_EVENT:
1046 ad1986a_automic(codec);
1047 break;
1048 }
1049}
1050
1051static int ad1986a_samsung_p50_init(struct hda_codec *codec)
1052{
1053 ad198x_init(codec);
1054 ad1986a_hp_automute(codec);
1055 ad1986a_automic(codec);
1056 return 0;
1057}
1058
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001059
Takashi Iwai9230d212006-03-13 13:49:49 +01001060/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001061enum {
1062 AD1986A_6STACK,
1063 AD1986A_3STACK,
1064 AD1986A_LAPTOP,
1065 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001066 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +01001067 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +01001068 AD1986A_SAMSUNG,
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001069 AD1986A_SAMSUNG_P50,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001070 AD1986A_MODELS
1071};
Takashi Iwai9230d212006-03-13 13:49:49 +01001072
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001073static const char *ad1986a_models[AD1986A_MODELS] = {
1074 [AD1986A_6STACK] = "6stack",
1075 [AD1986A_3STACK] = "3stack",
1076 [AD1986A_LAPTOP] = "laptop",
1077 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001078 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +01001079 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +01001080 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001081 [AD1986A_SAMSUNG_P50] = "samsung-p50",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001082};
1083
1084static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
1085 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001086 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001087 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001088 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001089 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1090 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1091 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1092 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001093 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001094 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001095 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1096 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1097 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1098 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1099 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001100 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Takashi Iwai7db756f2007-12-24 14:36:09 +01001101 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
Tobin Davis18768992007-03-12 22:20:51 +01001102 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001103 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001104 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
Tobin Davisf36090f2007-01-08 11:07:12 +01001105 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001106 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001107 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001108 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001109 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001110 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001111 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001112 {}
1113};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114
Takashi Iwaicb53c622007-08-10 17:21:45 +02001115#ifdef CONFIG_SND_HDA_POWER_SAVE
1116static struct hda_amp_list ad1986a_loopbacks[] = {
1117 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1118 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1119 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1120 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1121 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1122 { } /* end */
1123};
1124#endif
1125
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001126static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1127{
Takashi Iwai2f334f92009-02-20 14:37:42 +01001128 unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001129 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1130}
1131
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132static int patch_ad1986a(struct hda_codec *codec)
1133{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001134 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001135 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001137 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 if (spec == NULL)
1139 return -ENOMEM;
1140
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 codec->spec = spec;
1142
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001143 err = snd_hda_attach_beep_device(codec, 0x19);
1144 if (err < 0) {
1145 ad198x_free(codec);
1146 return err;
1147 }
1148 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1149
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 spec->multiout.max_channels = 6;
1151 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1152 spec->multiout.dac_nids = ad1986a_dac_nids;
1153 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001154 spec->num_adc_nids = 1;
1155 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001156 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001157 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001158 spec->num_mixers = 1;
1159 spec->mixers[0] = ad1986a_mixers;
1160 spec->num_init_verbs = 1;
1161 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001162#ifdef CONFIG_SND_HDA_POWER_SAVE
1163 spec->loopback.amplist = ad1986a_loopbacks;
1164#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001165 spec->vmaster_nid = 0x1b;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01001166 spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001168 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169
Takashi Iwai9230d212006-03-13 13:49:49 +01001170 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001171 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1172 ad1986a_models,
1173 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001174 switch (board_config) {
1175 case AD1986A_3STACK:
1176 spec->num_mixers = 2;
1177 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001178 spec->num_init_verbs = 2;
1179 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001180 spec->channel_mode = ad1986a_modes;
1181 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001182 spec->need_dac_fix = 1;
1183 spec->multiout.max_channels = 2;
1184 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001185 break;
1186 case AD1986A_LAPTOP:
1187 spec->mixers[0] = ad1986a_laptop_mixers;
1188 spec->multiout.max_channels = 2;
1189 spec->multiout.num_dacs = 1;
1190 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1191 break;
Takashi Iwai825aa972006-03-17 10:50:49 +01001192 case AD1986A_LAPTOP_EAPD:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001193 spec->num_mixers = 3;
1194 spec->mixers[0] = ad1986a_laptop_master_mixers;
1195 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1196 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001197 spec->num_init_verbs = 2;
1198 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1199 spec->multiout.max_channels = 2;
1200 spec->multiout.num_dacs = 1;
1201 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1202 if (!is_jack_available(codec, 0x25))
1203 spec->multiout.dig_out_nid = 0;
1204 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1205 break;
1206 case AD1986A_SAMSUNG:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001207 spec->num_mixers = 2;
1208 spec->mixers[0] = ad1986a_laptop_master_mixers;
1209 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001210 spec->num_init_verbs = 3;
Takashi Iwai825aa972006-03-17 10:50:49 +01001211 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001212 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa972006-03-17 10:50:49 +01001213 spec->multiout.max_channels = 2;
1214 spec->multiout.num_dacs = 1;
1215 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001216 if (!is_jack_available(codec, 0x25))
1217 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001218 spec->input_mux = &ad1986a_automic_capture_source;
1219 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1220 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa972006-03-17 10:50:49 +01001221 break;
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001222 case AD1986A_SAMSUNG_P50:
1223 spec->num_mixers = 2;
1224 spec->mixers[0] = ad1986a_automute_master_mixers;
1225 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1226 spec->num_init_verbs = 4;
1227 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1228 spec->init_verbs[2] = ad1986a_automic_verbs;
1229 spec->init_verbs[3] = ad1986a_hp_init_verbs;
1230 spec->multiout.max_channels = 2;
1231 spec->multiout.num_dacs = 1;
1232 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1233 if (!is_jack_available(codec, 0x25))
1234 spec->multiout.dig_out_nid = 0;
1235 spec->input_mux = &ad1986a_automic_capture_source;
1236 codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
1237 codec->patch_ops.init = ad1986a_samsung_p50_init;
1238 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001239 case AD1986A_LAPTOP_AUTOMUTE:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001240 spec->num_mixers = 3;
1241 spec->mixers[0] = ad1986a_automute_master_mixers;
1242 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1243 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001244 spec->num_init_verbs = 3;
1245 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1246 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1247 spec->multiout.max_channels = 2;
1248 spec->multiout.num_dacs = 1;
1249 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001250 if (!is_jack_available(codec, 0x25))
1251 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001252 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1253 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1254 codec->patch_ops.init = ad1986a_hp_init;
Takashi Iwai03c405a2009-06-24 14:10:15 +02001255 /* Lenovo N100 seems to report the reversed bit
1256 * for HP jack-sensing
1257 */
1258 spec->inv_jack_detect = 1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001259 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001260 case AD1986A_ULTRA:
1261 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1262 spec->num_init_verbs = 2;
1263 spec->init_verbs[1] = ad1986a_ultra_init;
1264 spec->multiout.max_channels = 2;
1265 spec->multiout.num_dacs = 1;
1266 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1267 spec->multiout.dig_out_nid = 0;
1268 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001269 }
1270
Takashi Iwaid29240c2007-10-26 12:35:56 +02001271 /* AD1986A has a hardware problem that it can't share a stream
1272 * with multiple output pins. The copy of front to surrounds
1273 * causes noisy or silent outputs at a certain timing, e.g.
1274 * changing the volume.
1275 * So, let's disable the shared stream.
1276 */
1277 spec->multiout.no_share_stream = 1;
1278
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 return 0;
1280}
1281
1282/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001283 * AD1983 specific
1284 */
1285
1286#define AD1983_SPDIF_OUT 0x02
1287#define AD1983_DAC 0x03
1288#define AD1983_ADC 0x04
1289
1290static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001291static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001292static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001293
1294static struct hda_input_mux ad1983_capture_source = {
1295 .num_items = 4,
1296 .items = {
1297 { "Mic", 0x0 },
1298 { "Line", 0x1 },
1299 { "Mix", 0x2 },
1300 { "Mix Mono", 0x3 },
1301 },
1302};
1303
1304/*
1305 * SPDIF playback route
1306 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001307static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001308{
1309 static char *texts[] = { "PCM", "ADC" };
1310
1311 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1312 uinfo->count = 1;
1313 uinfo->value.enumerated.items = 2;
1314 if (uinfo->value.enumerated.item > 1)
1315 uinfo->value.enumerated.item = 1;
1316 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1317 return 0;
1318}
1319
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001320static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001321{
1322 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1323 struct ad198x_spec *spec = codec->spec;
1324
1325 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1326 return 0;
1327}
1328
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001329static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001330{
1331 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1332 struct ad198x_spec *spec = codec->spec;
1333
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001334 if (ucontrol->value.enumerated.item[0] > 1)
1335 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001336 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1337 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001338 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1339 AC_VERB_SET_CONNECT_SEL,
1340 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001341 return 1;
1342 }
1343 return 0;
1344}
1345
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001346static struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001347 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1348 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1349 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1350 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1351 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1352 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1353 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1354 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1355 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1356 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1357 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1358 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001359 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
1360 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1361 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1362 {
1363 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1364 .name = "Capture Source",
1365 .info = ad198x_mux_enum_info,
1366 .get = ad198x_mux_enum_get,
1367 .put = ad198x_mux_enum_put,
1368 },
1369 {
1370 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001371 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001372 .info = ad1983_spdif_route_info,
1373 .get = ad1983_spdif_route_get,
1374 .put = ad1983_spdif_route_put,
1375 },
1376 { } /* end */
1377};
1378
1379static struct hda_verb ad1983_init_verbs[] = {
1380 /* Front, HP, Mono; mute as default */
1381 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1382 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1383 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1384 /* Beep, PCM, Mic, Line-In: mute */
1385 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1386 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1387 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1388 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1389 /* Front, HP selectors; from Mix */
1390 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1391 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1392 /* Mono selector; from Mix */
1393 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1394 /* Mic selector; Mic */
1395 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1396 /* Line-in selector: Line-in */
1397 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1398 /* Mic boost: 0dB */
1399 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1400 /* Record selector: mic */
1401 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1402 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1403 /* SPDIF route: PCM */
1404 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1405 /* Front Pin */
1406 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1407 /* HP Pin */
1408 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1409 /* Mono Pin */
1410 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1411 /* Mic Pin */
1412 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1413 /* Line Pin */
1414 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1415 { } /* end */
1416};
1417
Takashi Iwaicb53c622007-08-10 17:21:45 +02001418#ifdef CONFIG_SND_HDA_POWER_SAVE
1419static struct hda_amp_list ad1983_loopbacks[] = {
1420 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1421 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1422 { } /* end */
1423};
1424#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001425
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001426static int patch_ad1983(struct hda_codec *codec)
1427{
1428 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001429 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001430
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001431 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001432 if (spec == NULL)
1433 return -ENOMEM;
1434
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001435 codec->spec = spec;
1436
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001437 err = snd_hda_attach_beep_device(codec, 0x10);
1438 if (err < 0) {
1439 ad198x_free(codec);
1440 return err;
1441 }
1442 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1443
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001444 spec->multiout.max_channels = 2;
1445 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1446 spec->multiout.dac_nids = ad1983_dac_nids;
1447 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001448 spec->num_adc_nids = 1;
1449 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001450 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001451 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001452 spec->num_mixers = 1;
1453 spec->mixers[0] = ad1983_mixers;
1454 spec->num_init_verbs = 1;
1455 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001456 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001457#ifdef CONFIG_SND_HDA_POWER_SAVE
1458 spec->loopback.amplist = ad1983_loopbacks;
1459#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001460 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001461
1462 codec->patch_ops = ad198x_patch_ops;
1463
1464 return 0;
1465}
1466
1467
1468/*
1469 * AD1981 HD specific
1470 */
1471
1472#define AD1981_SPDIF_OUT 0x02
1473#define AD1981_DAC 0x03
1474#define AD1981_ADC 0x04
1475
1476static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001477static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001478static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001479
1480/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
1481static struct hda_input_mux ad1981_capture_source = {
1482 .num_items = 7,
1483 .items = {
1484 { "Front Mic", 0x0 },
1485 { "Line", 0x1 },
1486 { "Mix", 0x2 },
1487 { "Mix Mono", 0x3 },
1488 { "CD", 0x4 },
1489 { "Mic", 0x6 },
1490 { "Aux", 0x7 },
1491 },
1492};
1493
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001494static struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001495 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1496 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1497 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1498 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1499 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1500 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1501 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1502 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1503 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1504 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1505 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1506 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1507 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1508 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1509 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1510 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1511 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1512 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001513 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
1514 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
1515 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1516 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1517 {
1518 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1519 .name = "Capture Source",
1520 .info = ad198x_mux_enum_info,
1521 .get = ad198x_mux_enum_get,
1522 .put = ad198x_mux_enum_put,
1523 },
1524 /* identical with AD1983 */
1525 {
1526 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001527 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001528 .info = ad1983_spdif_route_info,
1529 .get = ad1983_spdif_route_get,
1530 .put = ad1983_spdif_route_put,
1531 },
1532 { } /* end */
1533};
1534
1535static struct hda_verb ad1981_init_verbs[] = {
1536 /* Front, HP, Mono; mute as default */
1537 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1538 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1539 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1540 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1541 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1542 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1543 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1544 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1545 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1546 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1547 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1548 /* Front, HP selectors; from Mix */
1549 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1550 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1551 /* Mono selector; from Mix */
1552 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1553 /* Mic Mixer; select Front Mic */
1554 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1555 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1556 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001557 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1558 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001559 /* Record selector: Front mic */
1560 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1561 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1562 /* SPDIF route: PCM */
1563 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1564 /* Front Pin */
1565 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1566 /* HP Pin */
1567 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1568 /* Mono Pin */
1569 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1570 /* Front & Rear Mic Pins */
1571 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1572 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1573 /* Line Pin */
1574 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1575 /* Digital Beep */
1576 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1577 /* Line-Out as Input: disabled */
1578 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1579 { } /* end */
1580};
1581
Takashi Iwaicb53c622007-08-10 17:21:45 +02001582#ifdef CONFIG_SND_HDA_POWER_SAVE
1583static struct hda_amp_list ad1981_loopbacks[] = {
1584 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1585 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1586 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1587 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1588 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1589 { } /* end */
1590};
1591#endif
1592
Takashi Iwai18a815d2006-03-01 19:54:39 +01001593/*
1594 * Patch for HP nx6320
1595 *
Tobin Davis18768992007-03-12 22:20:51 +01001596 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001597 * speaker output enabled _and_ mute-LED off.
1598 */
1599
1600#define AD1981_HP_EVENT 0x37
1601#define AD1981_MIC_EVENT 0x38
1602
1603static struct hda_verb ad1981_hp_init_verbs[] = {
1604 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1605 /* pin sensing on HP and Mic jacks */
1606 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1607 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1608 {}
1609};
1610
1611/* turn on/off EAPD (+ mute HP) as a master switch */
1612static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1613 struct snd_ctl_elem_value *ucontrol)
1614{
1615 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1616 struct ad198x_spec *spec = codec->spec;
1617
1618 if (! ad198x_eapd_put(kcontrol, ucontrol))
1619 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001620 /* change speaker pin appropriately */
1621 snd_hda_codec_write(codec, 0x05, 0,
1622 AC_VERB_SET_PIN_WIDGET_CONTROL,
1623 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001624 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001625 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1626 HDA_AMP_MUTE,
1627 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001628 return 1;
1629}
1630
1631/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02001632static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
1633 .ops = &snd_hda_bind_vol,
1634 .values = {
1635 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1636 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1637 0
1638 },
1639};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001640
1641/* mute internal speaker if HP is plugged */
1642static void ad1981_hp_automute(struct hda_codec *codec)
1643{
1644 unsigned int present;
1645
Takashi Iwaid56757a2009-11-18 08:00:14 +01001646 present = snd_hda_jack_detect(codec, 0x06);
Takashi Iwai47fd8302007-08-10 17:11:07 +02001647 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1648 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001649}
1650
1651/* toggle input of built-in and mic jack appropriately */
1652static void ad1981_hp_automic(struct hda_codec *codec)
1653{
1654 static struct hda_verb mic_jack_on[] = {
1655 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1656 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1657 {}
1658 };
1659 static struct hda_verb mic_jack_off[] = {
1660 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1661 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1662 {}
1663 };
1664 unsigned int present;
1665
Takashi Iwaid56757a2009-11-18 08:00:14 +01001666 present = snd_hda_jack_detect(codec, 0x08);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001667 if (present)
1668 snd_hda_sequence_write(codec, mic_jack_on);
1669 else
1670 snd_hda_sequence_write(codec, mic_jack_off);
1671}
1672
1673/* unsolicited event for HP jack sensing */
1674static void ad1981_hp_unsol_event(struct hda_codec *codec,
1675 unsigned int res)
1676{
1677 res >>= 26;
1678 switch (res) {
1679 case AD1981_HP_EVENT:
1680 ad1981_hp_automute(codec);
1681 break;
1682 case AD1981_MIC_EVENT:
1683 ad1981_hp_automic(codec);
1684 break;
1685 }
1686}
1687
1688static struct hda_input_mux ad1981_hp_capture_source = {
1689 .num_items = 3,
1690 .items = {
1691 { "Mic", 0x0 },
1692 { "Docking-Station", 0x1 },
1693 { "Mix", 0x2 },
1694 },
1695};
1696
1697static struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001698 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001699 {
1700 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001701 .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
Takashi Iwai18a815d2006-03-01 19:54:39 +01001702 .name = "Master Playback Switch",
1703 .info = ad198x_eapd_info,
1704 .get = ad198x_eapd_get,
1705 .put = ad1981_hp_master_sw_put,
1706 .private_value = 0x05,
1707 },
1708 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1709 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1710#if 0
1711 /* FIXME: analog mic/line loopback doesn't work with my tests...
1712 * (although recording is OK)
1713 */
1714 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1715 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1716 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1717 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1718 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1719 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1720 /* FIXME: does this laptop have analog CD connection? */
1721 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1722 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1723#endif
1724 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1725 HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
1726 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1727 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1728 {
1729 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1730 .name = "Capture Source",
1731 .info = ad198x_mux_enum_info,
1732 .get = ad198x_mux_enum_get,
1733 .put = ad198x_mux_enum_put,
1734 },
1735 { } /* end */
1736};
1737
1738/* initialize jack-sensing, too */
1739static int ad1981_hp_init(struct hda_codec *codec)
1740{
1741 ad198x_init(codec);
1742 ad1981_hp_automute(codec);
1743 ad1981_hp_automic(codec);
1744 return 0;
1745}
1746
Tobin Davis18768992007-03-12 22:20:51 +01001747/* configuration for Toshiba Laptops */
1748static struct hda_verb ad1981_toshiba_init_verbs[] = {
1749 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1750 /* pin sensing on HP and Mic jacks */
1751 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1752 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1753 {}
1754};
1755
1756static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
1757 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1758 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1759 { }
1760};
1761
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001762/* configuration for Lenovo Thinkpad T60 */
1763static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
1764 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1765 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1766 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1767 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1768 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1769 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1770 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1771 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1772 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1773 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1774 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1775 {
1776 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1777 .name = "Capture Source",
1778 .info = ad198x_mux_enum_info,
1779 .get = ad198x_mux_enum_get,
1780 .put = ad198x_mux_enum_put,
1781 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001782 /* identical with AD1983 */
1783 {
1784 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1785 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1786 .info = ad1983_spdif_route_info,
1787 .get = ad1983_spdif_route_get,
1788 .put = ad1983_spdif_route_put,
1789 },
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001790 { } /* end */
1791};
1792
1793static struct hda_input_mux ad1981_thinkpad_capture_source = {
1794 .num_items = 3,
1795 .items = {
1796 { "Mic", 0x0 },
1797 { "Mix", 0x2 },
1798 { "CD", 0x4 },
1799 },
1800};
1801
Takashi Iwai18a815d2006-03-01 19:54:39 +01001802/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001803enum {
1804 AD1981_BASIC,
1805 AD1981_HP,
1806 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001807 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001808 AD1981_MODELS
1809};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001810
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001811static const char *ad1981_models[AD1981_MODELS] = {
1812 [AD1981_HP] = "hp",
1813 [AD1981_THINKPAD] = "thinkpad",
1814 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001815 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001816};
1817
1818static struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001819 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001820 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001821 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001822 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001823 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001824 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001825 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001826 /* HP nx6320 (reversed SSID, H/W bug) */
1827 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001828 {}
1829};
1830
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001831static int patch_ad1981(struct hda_codec *codec)
1832{
1833 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001834 int err, board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001835
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001836 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001837 if (spec == NULL)
1838 return -ENOMEM;
1839
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001840 codec->spec = spec;
1841
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001842 err = snd_hda_attach_beep_device(codec, 0x10);
1843 if (err < 0) {
1844 ad198x_free(codec);
1845 return err;
1846 }
1847 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
1848
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001849 spec->multiout.max_channels = 2;
1850 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1851 spec->multiout.dac_nids = ad1981_dac_nids;
1852 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001853 spec->num_adc_nids = 1;
1854 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001855 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001856 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001857 spec->num_mixers = 1;
1858 spec->mixers[0] = ad1981_mixers;
1859 spec->num_init_verbs = 1;
1860 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001861 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001862#ifdef CONFIG_SND_HDA_POWER_SAVE
1863 spec->loopback.amplist = ad1981_loopbacks;
1864#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001865 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001866
1867 codec->patch_ops = ad198x_patch_ops;
1868
Takashi Iwai18a815d2006-03-01 19:54:39 +01001869 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001870 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1871 ad1981_models,
1872 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001873 switch (board_config) {
1874 case AD1981_HP:
1875 spec->mixers[0] = ad1981_hp_mixers;
1876 spec->num_init_verbs = 2;
1877 spec->init_verbs[1] = ad1981_hp_init_verbs;
1878 spec->multiout.dig_out_nid = 0;
1879 spec->input_mux = &ad1981_hp_capture_source;
1880
1881 codec->patch_ops.init = ad1981_hp_init;
1882 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
Daniel T Chen01f59662009-12-13 16:22:58 -05001883 /* set the upper-limit for mixer amp to 0dB for avoiding the
1884 * possible damage by overloading
1885 */
1886 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
1887 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
1888 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
1889 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
1890 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai18a815d2006-03-01 19:54:39 +01001891 break;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001892 case AD1981_THINKPAD:
1893 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001894 spec->input_mux = &ad1981_thinkpad_capture_source;
1895 break;
Tobin Davis18768992007-03-12 22:20:51 +01001896 case AD1981_TOSHIBA:
1897 spec->mixers[0] = ad1981_hp_mixers;
1898 spec->mixers[1] = ad1981_toshiba_mixers;
1899 spec->num_init_verbs = 2;
1900 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1901 spec->multiout.dig_out_nid = 0;
1902 spec->input_mux = &ad1981_hp_capture_source;
1903 codec->patch_ops.init = ad1981_hp_init;
1904 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1905 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001906 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001907 return 0;
1908}
1909
1910
1911/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001912 * AD1988
1913 *
1914 * Output pins and routes
1915 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001916 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001917 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1918 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1919 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1920 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1921 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1922 * port-F 0x16 (mute) <- 0x2a <- 06
1923 * port-G 0x24 (mute) <- 0x27 <- 05
1924 * port-H 0x25 (mute) <- 0x28 <- 0a
1925 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1926 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001927 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1928 * (*) 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 +01001929 *
1930 * Input pins and routes
1931 *
1932 * pin boost mix input # / adc input #
1933 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1934 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1935 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1936 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1937 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1938 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1939 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1940 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1941 *
1942 *
1943 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001944 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001945 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001946 *
1947 * Inputs of Analog Mix (0x20)
1948 * 0:Port-B (front mic)
1949 * 1:Port-C/G/H (line-in)
1950 * 2:Port-A
1951 * 3:Port-D (line-in/2)
1952 * 4:Port-E/G/H (mic-in)
1953 * 5:Port-F (mic2-in)
1954 * 6:CD
1955 * 7:Beep
1956 *
1957 * ADC selection
1958 * 0:Port-A
1959 * 1:Port-B (front mic-in)
1960 * 2:Port-C (line-in)
1961 * 3:Port-F (mic2-in)
1962 * 4:Port-E (mic-in)
1963 * 5:CD
1964 * 6:Port-G
1965 * 7:Port-H
1966 * 8:Port-D (line-in/2)
1967 * 9:Mix
1968 *
1969 * Proposed pin assignments by the datasheet
1970 *
1971 * 6-stack
1972 * Port-A front headphone
1973 * B front mic-in
1974 * C rear line-in
1975 * D rear front-out
1976 * E rear mic-in
1977 * F rear surround
1978 * G rear CLFE
1979 * H rear side
1980 *
1981 * 3-stack
1982 * Port-A front headphone
1983 * B front mic
1984 * C rear line-in/surround
1985 * D rear front-out
1986 * E rear mic-in/CLFE
1987 *
1988 * laptop
1989 * Port-A headphone
1990 * B mic-in
1991 * C docking station
1992 * D internal speaker (with EAPD)
1993 * E/F quad mic array
1994 */
1995
1996
1997/* models */
1998enum {
1999 AD1988_6STACK,
2000 AD1988_6STACK_DIG,
2001 AD1988_3STACK,
2002 AD1988_3STACK_DIG,
2003 AD1988_LAPTOP,
2004 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01002005 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002006 AD1988_MODEL_LAST,
2007};
2008
Takashi Iwaid32410b12005-11-24 16:06:23 +01002009/* reivision id to check workarounds */
2010#define AD1988A_REV2 0x100200
2011
Takashi Iwai1a806f42006-07-03 15:58:16 +02002012#define is_rev2(codec) \
2013 ((codec)->vendor_id == 0x11d41988 && \
2014 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002015
2016/*
2017 * mixers
2018 */
2019
Takashi Iwaid32410b12005-11-24 16:06:23 +01002020static hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002021 0x04, 0x06, 0x05, 0x0a
2022};
2023
Takashi Iwaid32410b12005-11-24 16:06:23 +01002024static hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002025 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002026};
2027
2028/* for AD1988A revision-2, DAC2-4 are swapped */
2029static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
2030 0x04, 0x05, 0x0a, 0x06
2031};
2032
2033static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002034 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002035};
2036
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002037static hda_nid_t ad1988_adc_nids[3] = {
2038 0x08, 0x09, 0x0f
2039};
2040
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002041static hda_nid_t ad1988_capsrc_nids[3] = {
2042 0x0c, 0x0d, 0x0e
2043};
2044
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002045#define AD1988_SPDIF_OUT 0x02
2046#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002047#define AD1988_SPDIF_IN 0x07
2048
Takashi Iwai3a08e302009-02-13 11:37:08 +01002049static hda_nid_t ad1989b_slave_dig_outs[] = {
2050 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002051};
2052
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002053static struct hda_input_mux ad1988_6stack_capture_source = {
2054 .num_items = 5,
2055 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002056 { "Front Mic", 0x1 }, /* port-B */
2057 { "Line", 0x2 }, /* port-C */
2058 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002059 { "CD", 0x5 },
2060 { "Mix", 0x9 },
2061 },
2062};
2063
2064static struct hda_input_mux ad1988_laptop_capture_source = {
2065 .num_items = 3,
2066 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002067 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002068 { "CD", 0x5 },
2069 { "Mix", 0x9 },
2070 },
2071};
2072
2073/*
2074 */
2075static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
2076 struct snd_ctl_elem_info *uinfo)
2077{
2078 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2079 struct ad198x_spec *spec = codec->spec;
2080 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
2081 spec->num_channel_mode);
2082}
2083
2084static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
2085 struct snd_ctl_elem_value *ucontrol)
2086{
2087 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2088 struct ad198x_spec *spec = codec->spec;
2089 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
2090 spec->num_channel_mode, spec->multiout.max_channels);
2091}
2092
2093static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
2094 struct snd_ctl_elem_value *ucontrol)
2095{
2096 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2097 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002098 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
2099 spec->num_channel_mode,
2100 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02002101 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02002102 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002103 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002104}
2105
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002106/* 6-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002107static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002108 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2109 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
2110 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2111 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
2112 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002113 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002114};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002115
Takashi Iwaid32410b12005-11-24 16:06:23 +01002116static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
2117 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2118 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2119 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2120 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2121 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002122 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002123};
2124
2125static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002126 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2127 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2128 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2129 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2130 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2131 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2132 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2133
2134 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2135 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2136 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2137 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2138 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2139 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2140 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2141 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2142
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002143 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002144 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2145
2146 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2147 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2148
2149 { } /* end */
2150};
2151
2152/* 3-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002153static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002154 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002155 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002156 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2157 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002158 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002159};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002160
Takashi Iwaid32410b12005-11-24 16:06:23 +01002161static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
2162 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002163 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2164 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2165 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002166 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002167};
2168
2169static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002170 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002171 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2172 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2173 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002174 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2175 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2176
2177 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2178 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2179 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2180 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2181 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2182 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2183 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2184 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2185
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002186 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002187 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2188
2189 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2190 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2191 {
2192 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2193 .name = "Channel Mode",
2194 .info = ad198x_ch_mode_info,
2195 .get = ad198x_ch_mode_get,
2196 .put = ad198x_ch_mode_put,
2197 },
2198
2199 { } /* end */
2200};
2201
2202/* laptop mode */
2203static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
2204 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2205 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2206 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2207
2208 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2209 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2210 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2211 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2212 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2213 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2214
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002215 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002216 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2217
2218 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2219
2220 {
2221 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2222 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002223 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01002224 .info = ad198x_eapd_info,
2225 .get = ad198x_eapd_get,
2226 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01002227 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002228 },
2229
2230 { } /* end */
2231};
2232
2233/* capture */
2234static struct snd_kcontrol_new ad1988_capture_mixers[] = {
2235 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2236 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2237 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2238 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2239 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2240 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2241 {
2242 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2243 /* The multiple "Capture Source" controls confuse alsamixer
2244 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002245 */
2246 /* .name = "Capture Source", */
2247 .name = "Input Source",
2248 .count = 3,
2249 .info = ad198x_mux_enum_info,
2250 .get = ad198x_mux_enum_get,
2251 .put = ad198x_mux_enum_put,
2252 },
2253 { } /* end */
2254};
2255
2256static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2257 struct snd_ctl_elem_info *uinfo)
2258{
2259 static char *texts[] = {
2260 "PCM", "ADC1", "ADC2", "ADC3"
2261 };
2262 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2263 uinfo->count = 1;
2264 uinfo->value.enumerated.items = 4;
2265 if (uinfo->value.enumerated.item >= 4)
2266 uinfo->value.enumerated.item = 3;
2267 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2268 return 0;
2269}
2270
2271static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2272 struct snd_ctl_elem_value *ucontrol)
2273{
2274 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2275 unsigned int sel;
2276
Takashi Iwaibddcf542007-07-24 18:04:05 +02002277 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2278 AC_AMP_GET_INPUT);
2279 if (!(sel & 0x80))
2280 ucontrol->value.enumerated.item[0] = 0;
2281 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002282 sel = snd_hda_codec_read(codec, 0x0b, 0,
2283 AC_VERB_GET_CONNECT_SEL, 0);
2284 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002285 sel++;
2286 else
2287 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002288 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002289 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002290 return 0;
2291}
2292
2293static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2294 struct snd_ctl_elem_value *ucontrol)
2295{
2296 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002297 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002298 int change;
2299
Takashi Iwai35b26722007-05-05 12:17:17 +02002300 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002301 if (val > 3)
2302 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002303 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002304 sel = snd_hda_codec_read(codec, 0x1d, 0,
2305 AC_VERB_GET_AMP_GAIN_MUTE,
2306 AC_AMP_GET_INPUT);
2307 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002308 if (change) {
2309 snd_hda_codec_write_cache(codec, 0x1d, 0,
2310 AC_VERB_SET_AMP_GAIN_MUTE,
2311 AMP_IN_UNMUTE(0));
2312 snd_hda_codec_write_cache(codec, 0x1d, 0,
2313 AC_VERB_SET_AMP_GAIN_MUTE,
2314 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002315 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002316 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002317 sel = snd_hda_codec_read(codec, 0x1d, 0,
2318 AC_VERB_GET_AMP_GAIN_MUTE,
2319 AC_AMP_GET_INPUT | 0x01);
2320 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002321 if (change) {
2322 snd_hda_codec_write_cache(codec, 0x1d, 0,
2323 AC_VERB_SET_AMP_GAIN_MUTE,
2324 AMP_IN_MUTE(0));
2325 snd_hda_codec_write_cache(codec, 0x1d, 0,
2326 AC_VERB_SET_AMP_GAIN_MUTE,
2327 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002328 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002329 sel = snd_hda_codec_read(codec, 0x0b, 0,
2330 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2331 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002332 if (change)
2333 snd_hda_codec_write_cache(codec, 0x0b, 0,
2334 AC_VERB_SET_CONNECT_SEL,
2335 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002336 }
2337 return change;
2338}
2339
2340static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
2341 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2342 {
2343 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2344 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002345 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002346 .info = ad1988_spdif_playback_source_info,
2347 .get = ad1988_spdif_playback_source_get,
2348 .put = ad1988_spdif_playback_source_put,
2349 },
2350 { } /* end */
2351};
2352
2353static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
2354 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2355 { } /* end */
2356};
2357
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002358static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
2359 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002360 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002361 { } /* end */
2362};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002363
2364/*
2365 * initialization verbs
2366 */
2367
2368/*
2369 * for 6-stack (+dig)
2370 */
2371static struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002372 /* Front, Surround, CLFE, side DAC; unmute as default */
2373 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2374 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2375 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2376 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002377 /* Port-A front headphon path */
2378 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2379 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2380 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2381 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2382 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2383 /* Port-D line-out path */
2384 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2385 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2386 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2387 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2388 /* Port-F surround path */
2389 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2390 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2391 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2392 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2393 /* Port-G CLFE path */
2394 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2395 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2396 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2397 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2398 /* Port-H side path */
2399 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2400 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2401 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2402 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2403 /* Mono out path */
2404 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2405 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2406 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2407 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2408 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2409 /* Port-B front mic-in path */
2410 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2411 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2412 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2413 /* Port-C line-in path */
2414 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2415 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2416 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2417 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2418 /* Port-E mic-in path */
2419 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2420 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2421 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2422 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002423 /* Analog CD Input */
2424 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002425 /* Analog Mix output amp */
2426 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002427
2428 { }
2429};
2430
2431static struct hda_verb ad1988_capture_init_verbs[] = {
2432 /* mute analog mix */
2433 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2434 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2435 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2436 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2437 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2438 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2439 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2440 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2441 /* select ADCs - front-mic */
2442 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2443 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2444 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002445
2446 { }
2447};
2448
2449static struct hda_verb ad1988_spdif_init_verbs[] = {
2450 /* SPDIF out sel */
2451 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2452 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2453 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002454 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002455 /* SPDIF out pin */
2456 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002457
2458 { }
2459};
2460
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002461static struct hda_verb ad1988_spdif_in_init_verbs[] = {
2462 /* unmute SPDIF input pin */
2463 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2464 { }
2465};
2466
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002467/* AD1989 has no ADC -> SPDIF route */
2468static struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002469 /* SPDIF-1 out pin */
2470 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002471 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002472 /* SPDIF-2/HDMI out pin */
2473 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2474 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002475 { }
2476};
2477
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002478/*
2479 * verbs for 3stack (+dig)
2480 */
2481static struct hda_verb ad1988_3stack_ch2_init[] = {
2482 /* set port-C to line-in */
2483 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2484 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2485 /* set port-E to mic-in */
2486 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2487 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2488 { } /* end */
2489};
2490
2491static struct hda_verb ad1988_3stack_ch6_init[] = {
2492 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002493 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002494 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002495 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002496 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002497 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002498 { } /* end */
2499};
2500
2501static struct hda_channel_mode ad1988_3stack_modes[2] = {
2502 { 2, ad1988_3stack_ch2_init },
2503 { 6, ad1988_3stack_ch6_init },
2504};
2505
2506static struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002507 /* Front, Surround, CLFE, side DAC; unmute as default */
2508 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2509 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2510 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2511 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002512 /* Port-A front headphon path */
2513 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2514 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2515 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2516 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2517 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2518 /* Port-D line-out path */
2519 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2520 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2521 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2522 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2523 /* Mono out path */
2524 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2525 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2526 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2527 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2528 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2529 /* Port-B front mic-in path */
2530 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2531 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2532 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002533 /* Port-C line-in/surround path - 6ch mode as default */
2534 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2535 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002536 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002537 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002538 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002539 /* Port-E mic-in/CLFE path - 6ch mode as default */
2540 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2541 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002542 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002543 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002544 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2545 /* mute analog mix */
2546 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2547 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2548 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2549 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2550 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2551 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2552 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2553 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2554 /* select ADCs - front-mic */
2555 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2556 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2557 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002558 /* Analog Mix output amp */
2559 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002560 { }
2561};
2562
2563/*
2564 * verbs for laptop mode (+dig)
2565 */
2566static struct hda_verb ad1988_laptop_hp_on[] = {
2567 /* unmute port-A and mute port-D */
2568 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2569 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2570 { } /* end */
2571};
2572static struct hda_verb ad1988_laptop_hp_off[] = {
2573 /* mute port-A and unmute port-D */
2574 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2575 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2576 { } /* end */
2577};
2578
2579#define AD1988_HP_EVENT 0x01
2580
2581static struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002582 /* Front, Surround, CLFE, side DAC; unmute as default */
2583 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2584 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2585 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2586 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002587 /* Port-A front headphon path */
2588 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2589 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2590 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2591 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2592 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2593 /* unsolicited event for pin-sense */
2594 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2595 /* Port-D line-out path + EAPD */
2596 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2597 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2598 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2599 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2600 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2601 /* Mono out path */
2602 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2603 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2604 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2605 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2606 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2607 /* Port-B mic-in path */
2608 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2609 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2610 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2611 /* Port-C docking station - try to output */
2612 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2613 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2614 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2615 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2616 /* mute analog mix */
2617 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2618 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2619 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2620 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2621 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2622 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2623 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2624 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2625 /* select ADCs - mic */
2626 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2627 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2628 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002629 /* Analog Mix output amp */
2630 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002631 { }
2632};
2633
2634static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2635{
2636 if ((res >> 26) != AD1988_HP_EVENT)
2637 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01002638 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002639 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2640 else
2641 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2642}
2643
Takashi Iwaicb53c622007-08-10 17:21:45 +02002644#ifdef CONFIG_SND_HDA_POWER_SAVE
2645static struct hda_amp_list ad1988_loopbacks[] = {
2646 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2647 { 0x20, HDA_INPUT, 1 }, /* Line */
2648 { 0x20, HDA_INPUT, 4 }, /* Mic */
2649 { 0x20, HDA_INPUT, 6 }, /* CD */
2650 { } /* end */
2651};
2652#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002653
2654/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002655 * Automatic parse of I/O pins from the BIOS configuration
2656 */
2657
Takashi Iwaid32410b12005-11-24 16:06:23 +01002658enum {
2659 AD_CTL_WIDGET_VOL,
2660 AD_CTL_WIDGET_MUTE,
2661 AD_CTL_BIND_MUTE,
2662};
2663static struct snd_kcontrol_new ad1988_control_templates[] = {
2664 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2665 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2666 HDA_BIND_MUTE(NULL, 0, 0, 0),
2667};
2668
2669/* add dynamic controls */
2670static int add_control(struct ad198x_spec *spec, int type, const char *name,
2671 unsigned long val)
2672{
2673 struct snd_kcontrol_new *knew;
2674
Takashi Iwai603c4012008-07-30 15:01:44 +02002675 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2676 knew = snd_array_new(&spec->kctls);
2677 if (!knew)
2678 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002679 *knew = ad1988_control_templates[type];
2680 knew->name = kstrdup(name, GFP_KERNEL);
2681 if (! knew->name)
2682 return -ENOMEM;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +01002683 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01002684 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002685 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002686 return 0;
2687}
2688
2689#define AD1988_PIN_CD_NID 0x18
2690#define AD1988_PIN_BEEP_NID 0x10
2691
2692static hda_nid_t ad1988_mixer_nids[8] = {
2693 /* A B C D E F G H */
2694 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2695};
2696
2697static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2698{
2699 static hda_nid_t idx_to_dac[8] = {
2700 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002701 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002702 };
2703 static hda_nid_t idx_to_dac_rev2[8] = {
2704 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002705 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002706 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002707 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002708 return idx_to_dac_rev2[idx];
2709 else
2710 return idx_to_dac[idx];
2711}
2712
2713static hda_nid_t ad1988_boost_nids[8] = {
2714 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2715};
2716
2717static int ad1988_pin_idx(hda_nid_t nid)
2718{
2719 static hda_nid_t ad1988_io_pins[8] = {
2720 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2721 };
2722 int i;
2723 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2724 if (ad1988_io_pins[i] == nid)
2725 return i;
2726 return 0; /* should be -1 */
2727}
2728
2729static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2730{
2731 static int loopback_idx[8] = {
2732 2, 0, 1, 3, 4, 5, 1, 4
2733 };
2734 switch (nid) {
2735 case AD1988_PIN_CD_NID:
2736 return 6;
2737 default:
2738 return loopback_idx[ad1988_pin_idx(nid)];
2739 }
2740}
2741
2742static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2743{
2744 static int adc_idx[8] = {
2745 0, 1, 2, 8, 4, 3, 6, 7
2746 };
2747 switch (nid) {
2748 case AD1988_PIN_CD_NID:
2749 return 5;
2750 default:
2751 return adc_idx[ad1988_pin_idx(nid)];
2752 }
2753}
2754
2755/* fill in the dac_nids table from the parsed pin configuration */
2756static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2757 const struct auto_pin_cfg *cfg)
2758{
2759 struct ad198x_spec *spec = codec->spec;
2760 int i, idx;
2761
2762 spec->multiout.dac_nids = spec->private_dac_nids;
2763
2764 /* check the pins hardwired to audio widget */
2765 for (i = 0; i < cfg->line_outs; i++) {
2766 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2767 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2768 }
2769 spec->multiout.num_dacs = cfg->line_outs;
2770 return 0;
2771}
2772
2773/* add playback controls from the parsed DAC table */
2774static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2775 const struct auto_pin_cfg *cfg)
2776{
2777 char name[32];
2778 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2779 hda_nid_t nid;
2780 int i, err;
2781
2782 for (i = 0; i < cfg->line_outs; i++) {
2783 hda_nid_t dac = spec->multiout.dac_nids[i];
2784 if (! dac)
2785 continue;
2786 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2787 if (i == 2) {
2788 /* Center/LFE */
2789 err = add_control(spec, AD_CTL_WIDGET_VOL,
2790 "Center Playback Volume",
2791 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2792 if (err < 0)
2793 return err;
2794 err = add_control(spec, AD_CTL_WIDGET_VOL,
2795 "LFE Playback Volume",
2796 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2797 if (err < 0)
2798 return err;
2799 err = add_control(spec, AD_CTL_BIND_MUTE,
2800 "Center Playback Switch",
2801 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2802 if (err < 0)
2803 return err;
2804 err = add_control(spec, AD_CTL_BIND_MUTE,
2805 "LFE Playback Switch",
2806 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2807 if (err < 0)
2808 return err;
2809 } else {
2810 sprintf(name, "%s Playback Volume", chname[i]);
2811 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2812 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2813 if (err < 0)
2814 return err;
2815 sprintf(name, "%s Playback Switch", chname[i]);
2816 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2817 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2818 if (err < 0)
2819 return err;
2820 }
2821 }
2822 return 0;
2823}
2824
2825/* add playback controls for speaker and HP outputs */
2826static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2827 const char *pfx)
2828{
2829 struct ad198x_spec *spec = codec->spec;
2830 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02002831 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002832 char name[32];
2833
2834 if (! pin)
2835 return 0;
2836
2837 idx = ad1988_pin_idx(pin);
2838 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02002839 /* check whether the corresponding DAC was already taken */
2840 for (i = 0; i < spec->autocfg.line_outs; i++) {
2841 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2842 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
2843 if (dac == nid)
2844 break;
2845 }
2846 if (i >= spec->autocfg.line_outs) {
2847 /* specify the DAC as the extra output */
2848 if (!spec->multiout.hp_nid)
2849 spec->multiout.hp_nid = nid;
2850 else
2851 spec->multiout.extra_out_nid[0] = nid;
2852 /* control HP volume/switch on the output mixer amp */
2853 sprintf(name, "%s Playback Volume", pfx);
2854 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2855 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2856 if (err < 0)
2857 return err;
2858 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01002859 nid = ad1988_mixer_nids[idx];
2860 sprintf(name, "%s Playback Switch", pfx);
2861 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2862 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2863 return err;
2864 return 0;
2865}
2866
2867/* create input playback/capture controls for the given pin */
2868static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2869 const char *ctlname, int boost)
2870{
2871 char name[32];
2872 int err, idx;
2873
2874 sprintf(name, "%s Playback Volume", ctlname);
2875 idx = ad1988_pin_to_loopback_idx(pin);
2876 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2877 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2878 return err;
2879 sprintf(name, "%s Playback Switch", ctlname);
2880 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2881 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2882 return err;
2883 if (boost) {
2884 hda_nid_t bnid;
2885 idx = ad1988_pin_idx(pin);
2886 bnid = ad1988_boost_nids[idx];
2887 if (bnid) {
2888 sprintf(name, "%s Boost", ctlname);
2889 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2890 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2891
2892 }
2893 }
2894 return 0;
2895}
2896
2897/* create playback/capture controls for input pins */
2898static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2899 const struct auto_pin_cfg *cfg)
2900{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002901 struct hda_input_mux *imux = &spec->private_imux;
2902 int i, err;
2903
2904 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002905 err = new_analog_input(spec, cfg->input_pins[i],
2906 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002907 i <= AUTO_PIN_FRONT_MIC);
2908 if (err < 0)
2909 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002910 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002911 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2912 imux->num_items++;
2913 }
2914 imux->items[imux->num_items].label = "Mix";
2915 imux->items[imux->num_items].index = 9;
2916 imux->num_items++;
2917
2918 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2919 "Analog Mix Playback Volume",
2920 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2921 return err;
2922 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2923 "Analog Mix Playback Switch",
2924 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2925 return err;
2926
2927 return 0;
2928}
2929
2930static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2931 hda_nid_t nid, int pin_type,
2932 int dac_idx)
2933{
2934 /* set as output */
2935 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2936 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2937 switch (nid) {
2938 case 0x11: /* port-A - DAC 04 */
2939 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2940 break;
2941 case 0x14: /* port-B - DAC 06 */
2942 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2943 break;
2944 case 0x15: /* port-C - DAC 05 */
2945 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2946 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002947 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002948 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2949 break;
2950 case 0x13: /* mono - DAC 04 */
2951 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2952 break;
2953 }
2954}
2955
2956static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2957{
2958 struct ad198x_spec *spec = codec->spec;
2959 int i;
2960
2961 for (i = 0; i < spec->autocfg.line_outs; i++) {
2962 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2963 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2964 }
2965}
2966
2967static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2968{
2969 struct ad198x_spec *spec = codec->spec;
2970 hda_nid_t pin;
2971
Takashi Iwai82bc9552006-03-21 11:24:42 +01002972 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002973 if (pin) /* connect to front */
2974 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002975 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002976 if (pin) /* connect to front */
2977 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2978}
2979
2980static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2981{
2982 struct ad198x_spec *spec = codec->spec;
2983 int i, idx;
2984
2985 for (i = 0; i < AUTO_PIN_LAST; i++) {
2986 hda_nid_t nid = spec->autocfg.input_pins[i];
2987 if (! nid)
2988 continue;
2989 switch (nid) {
2990 case 0x15: /* port-C */
2991 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2992 break;
2993 case 0x17: /* port-E */
2994 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2995 break;
2996 }
2997 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2998 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2999 if (nid != AD1988_PIN_CD_NID)
3000 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
3001 AMP_OUT_MUTE);
3002 idx = ad1988_pin_idx(nid);
3003 if (ad1988_boost_nids[idx])
3004 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
3005 AC_VERB_SET_AMP_GAIN_MUTE,
3006 AMP_OUT_ZERO);
3007 }
3008}
3009
3010/* parse the BIOS configuration and set up the alc_spec */
3011/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
3012static int ad1988_parse_auto_config(struct hda_codec *codec)
3013{
3014 struct ad198x_spec *spec = codec->spec;
3015 int err;
3016
Kailang Yangdf694da2005-12-05 19:42:22 +01003017 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003018 return err;
3019 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
3020 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01003021 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003022 return 0; /* can't find valid BIOS pin config */
3023 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01003024 (err = ad1988_auto_create_extra_out(codec,
3025 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003026 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003027 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003028 "Headphone")) < 0 ||
3029 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
3030 return err;
3031
3032 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3033
Takashi Iwai0852d7a2009-02-11 11:35:15 +01003034 if (spec->autocfg.dig_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003035 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3036 if (spec->autocfg.dig_in_pin)
3037 spec->dig_in_nid = AD1988_SPDIF_IN;
3038
Takashi Iwai603c4012008-07-30 15:01:44 +02003039 if (spec->kctls.list)
3040 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003041
3042 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
3043
3044 spec->input_mux = &spec->private_imux;
3045
3046 return 1;
3047}
3048
3049/* init callback for auto-configuration model -- overriding the default init */
3050static int ad1988_auto_init(struct hda_codec *codec)
3051{
3052 ad198x_init(codec);
3053 ad1988_auto_init_multi_out(codec);
3054 ad1988_auto_init_extra_out(codec);
3055 ad1988_auto_init_analog_input(codec);
3056 return 0;
3057}
3058
3059
3060/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003061 */
3062
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003063static const char *ad1988_models[AD1988_MODEL_LAST] = {
3064 [AD1988_6STACK] = "6stack",
3065 [AD1988_6STACK_DIG] = "6stack-dig",
3066 [AD1988_3STACK] = "3stack",
3067 [AD1988_3STACK_DIG] = "3stack-dig",
3068 [AD1988_LAPTOP] = "laptop",
3069 [AD1988_LAPTOP_DIG] = "laptop-dig",
3070 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003071};
3072
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003073static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01003074 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01003075 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02003076 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07003077 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003078 {}
3079};
3080
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003081static int patch_ad1988(struct hda_codec *codec)
3082{
3083 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003084 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003085
3086 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3087 if (spec == NULL)
3088 return -ENOMEM;
3089
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003090 codec->spec = spec;
3091
Takashi Iwai1a806f42006-07-03 15:58:16 +02003092 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003093 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
3094
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003095 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003096 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003097 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02003098 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3099 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003100 board_config = AD1988_AUTO;
3101 }
3102
3103 if (board_config == AD1988_AUTO) {
3104 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003105 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003106 if (err < 0) {
3107 ad198x_free(codec);
3108 return err;
3109 } else if (! err) {
3110 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
3111 board_config = AD1988_6STACK;
3112 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003113 }
3114
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003115 err = snd_hda_attach_beep_device(codec, 0x10);
3116 if (err < 0) {
3117 ad198x_free(codec);
3118 return err;
3119 }
3120 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3121
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003122 switch (board_config) {
3123 case AD1988_6STACK:
3124 case AD1988_6STACK_DIG:
3125 spec->multiout.max_channels = 8;
3126 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003127 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003128 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3129 else
3130 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003131 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003132 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003133 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003134 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3135 else
3136 spec->mixers[0] = ad1988_6stack_mixers1;
3137 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003138 spec->num_init_verbs = 1;
3139 spec->init_verbs[0] = ad1988_6stack_init_verbs;
3140 if (board_config == AD1988_6STACK_DIG) {
3141 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3142 spec->dig_in_nid = AD1988_SPDIF_IN;
3143 }
3144 break;
3145 case AD1988_3STACK:
3146 case AD1988_3STACK_DIG:
3147 spec->multiout.max_channels = 6;
3148 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003149 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003150 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3151 else
3152 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003153 spec->input_mux = &ad1988_6stack_capture_source;
3154 spec->channel_mode = ad1988_3stack_modes;
3155 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003156 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003157 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003158 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3159 else
3160 spec->mixers[0] = ad1988_3stack_mixers1;
3161 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003162 spec->num_init_verbs = 1;
3163 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3164 if (board_config == AD1988_3STACK_DIG)
3165 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3166 break;
3167 case AD1988_LAPTOP:
3168 case AD1988_LAPTOP_DIG:
3169 spec->multiout.max_channels = 2;
3170 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003171 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003172 spec->input_mux = &ad1988_laptop_capture_source;
3173 spec->num_mixers = 1;
3174 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01003175 spec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003176 spec->num_init_verbs = 1;
3177 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3178 if (board_config == AD1988_LAPTOP_DIG)
3179 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3180 break;
3181 }
3182
Takashi Iwaid32410b12005-11-24 16:06:23 +01003183 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3184 spec->adc_nids = ad1988_adc_nids;
3185 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003186 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3187 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3188 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003189 if (codec->vendor_id >= 0x11d4989a) {
3190 spec->mixers[spec->num_mixers++] =
3191 ad1989_spdif_out_mixers;
3192 spec->init_verbs[spec->num_init_verbs++] =
3193 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003194 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003195 } else {
3196 spec->mixers[spec->num_mixers++] =
3197 ad1988_spdif_out_mixers;
3198 spec->init_verbs[spec->num_init_verbs++] =
3199 ad1988_spdif_init_verbs;
3200 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003201 }
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003202 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003203 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003204 spec->init_verbs[spec->num_init_verbs++] =
3205 ad1988_spdif_in_init_verbs;
3206 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003207
3208 codec->patch_ops = ad198x_patch_ops;
3209 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003210 case AD1988_AUTO:
3211 codec->patch_ops.init = ad1988_auto_init;
3212 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003213 case AD1988_LAPTOP:
3214 case AD1988_LAPTOP_DIG:
3215 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3216 break;
3217 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003218#ifdef CONFIG_SND_HDA_POWER_SAVE
3219 spec->loopback.amplist = ad1988_loopbacks;
3220#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003221 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003222
3223 return 0;
3224}
3225
3226
3227/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003228 * AD1884 / AD1984
3229 *
3230 * port-B - front line/mic-in
3231 * port-E - aux in/out
3232 * port-F - aux in/out
3233 * port-C - rear line/mic-in
3234 * port-D - rear line/hp-out
3235 * port-A - front line/hp-out
3236 *
3237 * AD1984 = AD1884 + two digital mic-ins
3238 *
3239 * FIXME:
3240 * For simplicity, we share the single DAC for both HP and line-outs
3241 * right now. The inidividual playbacks could be easily implemented,
3242 * but no build-up framework is given, so far.
3243 */
3244
3245static hda_nid_t ad1884_dac_nids[1] = {
3246 0x04,
3247};
3248
3249static hda_nid_t ad1884_adc_nids[2] = {
3250 0x08, 0x09,
3251};
3252
3253static hda_nid_t ad1884_capsrc_nids[2] = {
3254 0x0c, 0x0d,
3255};
3256
3257#define AD1884_SPDIF_OUT 0x02
3258
3259static struct hda_input_mux ad1884_capture_source = {
3260 .num_items = 4,
3261 .items = {
3262 { "Front Mic", 0x0 },
3263 { "Mic", 0x1 },
3264 { "CD", 0x2 },
3265 { "Mix", 0x3 },
3266 },
3267};
3268
3269static struct snd_kcontrol_new ad1884_base_mixers[] = {
3270 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3271 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3272 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3273 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3274 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3275 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3276 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3277 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3278 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3279 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3280 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3281 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003282 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
3283 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3284 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3285 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3286 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3287 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3288 {
3289 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3290 /* The multiple "Capture Source" controls confuse alsamixer
3291 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003292 */
3293 /* .name = "Capture Source", */
3294 .name = "Input Source",
3295 .count = 2,
3296 .info = ad198x_mux_enum_info,
3297 .get = ad198x_mux_enum_get,
3298 .put = ad198x_mux_enum_put,
3299 },
3300 /* SPDIF controls */
3301 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3302 {
3303 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3304 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3305 /* identical with ad1983 */
3306 .info = ad1983_spdif_route_info,
3307 .get = ad1983_spdif_route_get,
3308 .put = ad1983_spdif_route_put,
3309 },
3310 { } /* end */
3311};
3312
3313static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3314 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3315 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3316 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003317 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003318 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003319 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003320 { } /* end */
3321};
3322
3323/*
3324 * initialization verbs
3325 */
3326static struct hda_verb ad1884_init_verbs[] = {
3327 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003328 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3329 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003330 /* Port-A (HP) mixer */
3331 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3332 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3333 /* Port-A pin */
3334 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3335 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3336 /* HP selector - select DAC2 */
3337 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3338 /* Port-D (Line-out) mixer */
3339 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3340 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3341 /* Port-D pin */
3342 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3343 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3344 /* Mono-out mixer */
3345 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3346 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3347 /* Mono-out pin */
3348 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3349 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3350 /* Mono selector */
3351 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3352 /* Port-B (front mic) pin */
3353 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003354 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003355 /* Port-C (rear mic) pin */
3356 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003357 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003358 /* Analog mixer; mute as default */
3359 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3360 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3361 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3362 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3363 /* Analog Mix output amp */
3364 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3365 /* SPDIF output selector */
3366 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3367 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3368 { } /* end */
3369};
3370
Takashi Iwaicb53c622007-08-10 17:21:45 +02003371#ifdef CONFIG_SND_HDA_POWER_SAVE
3372static struct hda_amp_list ad1884_loopbacks[] = {
3373 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3374 { 0x20, HDA_INPUT, 1 }, /* Mic */
3375 { 0x20, HDA_INPUT, 2 }, /* CD */
3376 { 0x20, HDA_INPUT, 4 }, /* Docking */
3377 { } /* end */
3378};
3379#endif
3380
Takashi Iwai2134ea42008-01-10 16:53:55 +01003381static const char *ad1884_slave_vols[] = {
3382 "PCM Playback Volume",
3383 "Mic Playback Volume",
3384 "Mono Playback Volume",
3385 "Front Mic Playback Volume",
3386 "Mic Playback Volume",
3387 "CD Playback Volume",
3388 "Internal Mic Playback Volume",
Akinobu Mitabca68462009-04-06 18:42:42 +09003389 "Docking Mic Playback Volume",
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003390 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003391 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003392 NULL
3393};
3394
Takashi Iwai2bac6472007-05-18 18:21:41 +02003395static int patch_ad1884(struct hda_codec *codec)
3396{
3397 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003398 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003399
3400 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3401 if (spec == NULL)
3402 return -ENOMEM;
3403
Takashi Iwai2bac6472007-05-18 18:21:41 +02003404 codec->spec = spec;
3405
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003406 err = snd_hda_attach_beep_device(codec, 0x10);
3407 if (err < 0) {
3408 ad198x_free(codec);
3409 return err;
3410 }
3411 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3412
Takashi Iwai2bac6472007-05-18 18:21:41 +02003413 spec->multiout.max_channels = 2;
3414 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3415 spec->multiout.dac_nids = ad1884_dac_nids;
3416 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3417 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3418 spec->adc_nids = ad1884_adc_nids;
3419 spec->capsrc_nids = ad1884_capsrc_nids;
3420 spec->input_mux = &ad1884_capture_source;
3421 spec->num_mixers = 1;
3422 spec->mixers[0] = ad1884_base_mixers;
3423 spec->num_init_verbs = 1;
3424 spec->init_verbs[0] = ad1884_init_verbs;
3425 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003426#ifdef CONFIG_SND_HDA_POWER_SAVE
3427 spec->loopback.amplist = ad1884_loopbacks;
3428#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003429 spec->vmaster_nid = 0x04;
3430 /* we need to cover all playback volumes */
3431 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003432
3433 codec->patch_ops = ad198x_patch_ops;
3434
3435 return 0;
3436}
3437
3438/*
3439 * Lenovo Thinkpad T61/X61
3440 */
3441static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003442 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003443 .items = {
3444 { "Mic", 0x0 },
3445 { "Internal Mic", 0x1 },
3446 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003447 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003448 },
3449};
3450
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003451
3452/*
3453 * Dell Precision T3400
3454 */
3455static struct hda_input_mux ad1984_dell_desktop_capture_source = {
3456 .num_items = 3,
3457 .items = {
3458 { "Front Mic", 0x0 },
3459 { "Line-In", 0x1 },
3460 { "Mix", 0x3 },
3461 },
3462};
3463
3464
Takashi Iwai2bac6472007-05-18 18:21:41 +02003465static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3466 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3467 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3468 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3469 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3470 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3471 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3472 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3473 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3474 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3475 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003476 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3477 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003478 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003479 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3480 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3481 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3482 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3483 {
3484 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3485 /* The multiple "Capture Source" controls confuse alsamixer
3486 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003487 */
3488 /* .name = "Capture Source", */
3489 .name = "Input Source",
3490 .count = 2,
3491 .info = ad198x_mux_enum_info,
3492 .get = ad198x_mux_enum_get,
3493 .put = ad198x_mux_enum_put,
3494 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003495 /* SPDIF controls */
3496 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3497 {
3498 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3499 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3500 /* identical with ad1983 */
3501 .info = ad1983_spdif_route_info,
3502 .get = ad1983_spdif_route_get,
3503 .put = ad1983_spdif_route_put,
3504 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003505 { } /* end */
3506};
3507
3508/* additional verbs */
3509static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3510 /* Port-E (docking station mic) pin */
3511 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3512 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3513 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003514 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003515 /* Analog mixer - docking mic; mute as default */
3516 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003517 /* enable EAPD bit */
3518 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003519 { } /* end */
3520};
3521
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003522/*
3523 * Dell Precision T3400
3524 */
3525static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
3526 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3527 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3528 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3529 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3530 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3531 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3532 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3533 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3534 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003535 HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
3536 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3537 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3538 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3539 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3540 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3541 {
3542 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3543 /* The multiple "Capture Source" controls confuse alsamixer
3544 * So call somewhat different..
3545 */
3546 /* .name = "Capture Source", */
3547 .name = "Input Source",
3548 .count = 2,
3549 .info = ad198x_mux_enum_info,
3550 .get = ad198x_mux_enum_get,
3551 .put = ad198x_mux_enum_put,
3552 },
3553 { } /* end */
3554};
3555
Takashi Iwai2bac6472007-05-18 18:21:41 +02003556/* Digial MIC ADC NID 0x05 + 0x06 */
3557static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3558 struct hda_codec *codec,
3559 unsigned int stream_tag,
3560 unsigned int format,
3561 struct snd_pcm_substream *substream)
3562{
3563 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3564 stream_tag, 0, format);
3565 return 0;
3566}
3567
3568static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3569 struct hda_codec *codec,
3570 struct snd_pcm_substream *substream)
3571{
Takashi Iwai888afa12008-03-18 09:57:50 +01003572 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003573 return 0;
3574}
3575
3576static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3577 .substreams = 2,
3578 .channels_min = 2,
3579 .channels_max = 2,
3580 .nid = 0x05,
3581 .ops = {
3582 .prepare = ad1984_pcm_dmic_prepare,
3583 .cleanup = ad1984_pcm_dmic_cleanup
3584 },
3585};
3586
3587static int ad1984_build_pcms(struct hda_codec *codec)
3588{
3589 struct ad198x_spec *spec = codec->spec;
3590 struct hda_pcm *info;
3591 int err;
3592
3593 err = ad198x_build_pcms(codec);
3594 if (err < 0)
3595 return err;
3596
3597 info = spec->pcm_rec + codec->num_pcms;
3598 codec->num_pcms++;
3599 info->name = "AD1984 Digital Mic";
3600 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3601 return 0;
3602}
3603
3604/* models */
3605enum {
3606 AD1984_BASIC,
3607 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003608 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003609 AD1984_MODELS
3610};
3611
3612static const char *ad1984_models[AD1984_MODELS] = {
3613 [AD1984_BASIC] = "basic",
3614 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003615 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003616};
3617
3618static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3619 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003620 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003621 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003622 {}
3623};
3624
3625static int patch_ad1984(struct hda_codec *codec)
3626{
3627 struct ad198x_spec *spec;
3628 int board_config, err;
3629
3630 err = patch_ad1884(codec);
3631 if (err < 0)
3632 return err;
3633 spec = codec->spec;
3634 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3635 ad1984_models, ad1984_cfg_tbl);
3636 switch (board_config) {
3637 case AD1984_BASIC:
3638 /* additional digital mics */
3639 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3640 codec->patch_ops.build_pcms = ad1984_build_pcms;
3641 break;
3642 case AD1984_THINKPAD:
Jerone Youngebf00c52008-01-07 12:22:18 +01003643 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003644 spec->input_mux = &ad1984_thinkpad_capture_source;
3645 spec->mixers[0] = ad1984_thinkpad_mixers;
3646 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
3647 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003648 case AD1984_DELL_DESKTOP:
3649 spec->multiout.dig_out_nid = 0;
3650 spec->input_mux = &ad1984_dell_desktop_capture_source;
3651 spec->mixers[0] = ad1984_dell_desktop_mixers;
3652 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003653 }
3654 return 0;
3655}
3656
3657
3658/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003659 * AD1883 / AD1884A / AD1984A / AD1984B
3660 *
3661 * port-B (0x14) - front mic-in
3662 * port-E (0x1c) - rear mic-in
3663 * port-F (0x16) - CD / ext out
3664 * port-C (0x15) - rear line-in
3665 * port-D (0x12) - rear line-out
3666 * port-A (0x11) - front hp-out
3667 *
3668 * AD1984A = AD1884A + digital-mic
3669 * AD1883 = equivalent with AD1984A
3670 * AD1984B = AD1984A + extra SPDIF-out
3671 *
3672 * FIXME:
3673 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3674 */
3675
3676static hda_nid_t ad1884a_dac_nids[1] = {
3677 0x03,
3678};
3679
3680#define ad1884a_adc_nids ad1884_adc_nids
3681#define ad1884a_capsrc_nids ad1884_capsrc_nids
3682
3683#define AD1884A_SPDIF_OUT 0x02
3684
3685static struct hda_input_mux ad1884a_capture_source = {
3686 .num_items = 5,
3687 .items = {
3688 { "Front Mic", 0x0 },
3689 { "Mic", 0x4 },
3690 { "Line", 0x1 },
3691 { "CD", 0x2 },
3692 { "Mix", 0x3 },
3693 },
3694};
3695
3696static struct snd_kcontrol_new ad1884a_base_mixers[] = {
3697 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3698 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3699 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3700 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3701 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3702 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3703 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3704 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3705 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3706 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3707 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3708 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3709 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3710 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3711 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3712 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003713 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3714 HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
3715 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3716 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3717 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3718 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3719 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3720 {
3721 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3722 /* The multiple "Capture Source" controls confuse alsamixer
3723 * So call somewhat different..
3724 */
3725 /* .name = "Capture Source", */
3726 .name = "Input Source",
3727 .count = 2,
3728 .info = ad198x_mux_enum_info,
3729 .get = ad198x_mux_enum_get,
3730 .put = ad198x_mux_enum_put,
3731 },
3732 /* SPDIF controls */
3733 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3734 {
3735 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3736 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3737 /* identical with ad1983 */
3738 .info = ad1983_spdif_route_info,
3739 .get = ad1983_spdif_route_get,
3740 .put = ad1983_spdif_route_put,
3741 },
3742 { } /* end */
3743};
3744
3745/*
3746 * initialization verbs
3747 */
3748static struct hda_verb ad1884a_init_verbs[] = {
3749 /* DACs; unmute as default */
3750 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3751 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3752 /* Port-A (HP) mixer - route only from analog mixer */
3753 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3754 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3755 /* Port-A pin */
3756 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3757 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3758 /* Port-D (Line-out) mixer - route only from analog mixer */
3759 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3760 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3761 /* Port-D pin */
3762 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3763 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3764 /* Mono-out mixer - route only from analog mixer */
3765 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3766 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3767 /* Mono-out pin */
3768 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3769 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3770 /* Port-B (front mic) pin */
3771 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003772 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003773 /* Port-C (rear line-in) pin */
3774 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003775 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003776 /* Port-E (rear mic) pin */
3777 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3778 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3779 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
3780 /* Port-F (CD) pin */
3781 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3782 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3783 /* Analog mixer; mute as default */
3784 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3785 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3786 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3787 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3788 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
3789 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3790 /* Analog Mix output amp */
3791 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3792 /* capture sources */
3793 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
3794 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3795 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3796 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3797 /* SPDIF output amp */
3798 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3799 { } /* end */
3800};
3801
3802#ifdef CONFIG_SND_HDA_POWER_SAVE
3803static struct hda_amp_list ad1884a_loopbacks[] = {
3804 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3805 { 0x20, HDA_INPUT, 1 }, /* Mic */
3806 { 0x20, HDA_INPUT, 2 }, /* CD */
3807 { 0x20, HDA_INPUT, 4 }, /* Docking */
3808 { } /* end */
3809};
3810#endif
3811
3812/*
3813 * Laptop model
3814 *
3815 * Port A: Headphone jack
3816 * Port B: MIC jack
3817 * Port C: Internal MIC
3818 * Port D: Dock Line Out (if enabled)
3819 * Port E: Dock Line In (if enabled)
3820 * Port F: Internal speakers
3821 */
3822
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003823static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
3824 struct snd_ctl_elem_value *ucontrol)
3825{
3826 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3827 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
3828 int mute = (!ucontrol->value.integer.value[0] &&
3829 !ucontrol->value.integer.value[1]);
3830 /* toggle GPIO1 according to the mute state */
3831 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
3832 mute ? 0x02 : 0x0);
3833 return ret;
3834}
Takashi Iwaic5059252008-02-16 09:43:56 +01003835
3836static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
3837 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003838 {
3839 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3840 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003841 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003842 .info = snd_hda_mixer_amp_switch_info,
3843 .get = snd_hda_mixer_amp_switch_get,
3844 .put = ad1884a_mobile_master_sw_put,
3845 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3846 },
Takashi Iwaic5059252008-02-16 09:43:56 +01003847 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3848 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3849 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3850 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3851 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3852 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3853 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3854 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3855 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003856 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3857 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
3858 HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3859 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3860 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003861 { } /* end */
3862};
3863
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003864static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
3865 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db17e2009-07-02 16:10:23 +02003866 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
3867 {
3868 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3869 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003870 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai099db17e2009-07-02 16:10:23 +02003871 .info = snd_hda_mixer_amp_switch_info,
3872 .get = snd_hda_mixer_amp_switch_get,
3873 .put = ad1884a_mobile_master_sw_put,
3874 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3875 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003876 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3877 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02003878 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
3879 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003880 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3881 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003882 { } /* end */
3883};
3884
Takashi Iwaic5059252008-02-16 09:43:56 +01003885/* mute internal speaker if HP is plugged */
3886static void ad1884a_hp_automute(struct hda_codec *codec)
3887{
3888 unsigned int present;
3889
Takashi Iwaid56757a2009-11-18 08:00:14 +01003890 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01003891 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3892 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3893 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3894 present ? 0x00 : 0x02);
3895}
3896
Takashi Iwai269ef192008-05-30 15:32:15 +02003897/* switch to external mic if plugged */
3898static void ad1884a_hp_automic(struct hda_codec *codec)
3899{
3900 unsigned int present;
3901
Takashi Iwaid56757a2009-11-18 08:00:14 +01003902 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02003903 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
3904 present ? 0 : 1);
3905}
3906
Takashi Iwaic5059252008-02-16 09:43:56 +01003907#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02003908#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01003909
3910/* unsolicited event for HP jack sensing */
3911static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
3912{
Takashi Iwai269ef192008-05-30 15:32:15 +02003913 switch (res >> 26) {
3914 case AD1884A_HP_EVENT:
3915 ad1884a_hp_automute(codec);
3916 break;
3917 case AD1884A_MIC_EVENT:
3918 ad1884a_hp_automic(codec);
3919 break;
3920 }
Takashi Iwaic5059252008-02-16 09:43:56 +01003921}
3922
3923/* initialize jack-sensing, too */
3924static int ad1884a_hp_init(struct hda_codec *codec)
3925{
3926 ad198x_init(codec);
3927 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02003928 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01003929 return 0;
3930}
3931
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003932/* mute internal speaker if HP or docking HP is plugged */
3933static void ad1884a_laptop_automute(struct hda_codec *codec)
3934{
3935 unsigned int present;
3936
Takashi Iwaid56757a2009-11-18 08:00:14 +01003937 present = snd_hda_jack_detect(codec, 0x11);
3938 if (!present)
3939 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003940 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3941 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3942 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3943 present ? 0x00 : 0x02);
3944}
3945
3946/* switch to external mic if plugged */
3947static void ad1884a_laptop_automic(struct hda_codec *codec)
3948{
3949 unsigned int idx;
3950
Takashi Iwaid56757a2009-11-18 08:00:14 +01003951 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003952 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01003953 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003954 idx = 4;
3955 else
3956 idx = 1;
3957 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
3958}
3959
3960/* unsolicited event for HP jack sensing */
3961static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
3962 unsigned int res)
3963{
3964 switch (res >> 26) {
3965 case AD1884A_HP_EVENT:
3966 ad1884a_laptop_automute(codec);
3967 break;
3968 case AD1884A_MIC_EVENT:
3969 ad1884a_laptop_automic(codec);
3970 break;
3971 }
3972}
3973
3974/* initialize jack-sensing, too */
3975static int ad1884a_laptop_init(struct hda_codec *codec)
3976{
3977 ad198x_init(codec);
3978 ad1884a_laptop_automute(codec);
3979 ad1884a_laptop_automic(codec);
3980 return 0;
3981}
3982
Takashi Iwaic5059252008-02-16 09:43:56 +01003983/* additional verbs for laptop model */
3984static struct hda_verb ad1884a_laptop_verbs[] = {
3985 /* Port-A (HP) pin - always unmuted */
3986 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3987 /* Port-F (int speaker) mixer - route only from analog mixer */
3988 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3989 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08003990 /* Port-F (int speaker) pin */
3991 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01003992 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08003993 /* required for compaq 6530s/6531s speaker output */
3994 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003995 /* Port-C pin - internal mic-in */
3996 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3997 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3998 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02003999 /* Port-D (docking line-out) pin - default unmuted */
4000 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01004001 /* analog mix */
4002 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4003 /* unsolicited event for pin-sense */
4004 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004005 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004006 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004007 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02004008 /* allow to touch GPIO1 (for mute control) */
4009 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4010 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4011 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01004012 { } /* end */
4013};
4014
Takashi Iwai73156132009-04-23 08:24:48 +02004015static struct hda_verb ad1884a_mobile_verbs[] = {
4016 /* DACs; unmute as default */
4017 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4018 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4019 /* Port-A (HP) mixer - route only from analog mixer */
4020 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4021 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4022 /* Port-A pin */
4023 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4024 /* Port-A (HP) pin - always unmuted */
4025 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4026 /* Port-B (mic jack) pin */
4027 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4028 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4029 /* Port-C (int mic) pin */
4030 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4031 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4032 /* Port-F (int speaker) mixer - route only from analog mixer */
4033 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4034 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4035 /* Port-F pin */
4036 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4037 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4038 /* Analog mixer; mute as default */
4039 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4040 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4041 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4042 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4043 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4044 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4045 /* Analog Mix output amp */
4046 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4047 /* capture sources */
4048 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4049 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4050 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4051 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4052 /* unsolicited event for pin-sense */
4053 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4054 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db17e2009-07-02 16:10:23 +02004055 /* allow to touch GPIO1 (for mute control) */
4056 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4057 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4058 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02004059 { } /* end */
4060};
4061
Takashi Iwaic5059252008-02-16 09:43:56 +01004062/*
Takashi Iwaif0813742008-03-18 12:13:03 +01004063 * Thinkpad X300
4064 * 0x11 - HP
4065 * 0x12 - speaker
4066 * 0x14 - mic-in
4067 * 0x17 - built-in mic
4068 */
4069
4070static struct hda_verb ad1984a_thinkpad_verbs[] = {
4071 /* HP unmute */
4072 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4073 /* analog mix */
4074 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4075 /* turn on EAPD */
4076 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4077 /* unsolicited event for pin-sense */
4078 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4079 /* internal mic - dmic */
4080 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02004081 /* set magic COEFs for dmic */
4082 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4083 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01004084 { } /* end */
4085};
4086
4087static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
4088 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4089 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4090 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4091 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4092 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4093 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01004094 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
4095 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4096 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4097 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4098 {
4099 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4100 .name = "Capture Source",
4101 .info = ad198x_mux_enum_info,
4102 .get = ad198x_mux_enum_get,
4103 .put = ad198x_mux_enum_put,
4104 },
4105 { } /* end */
4106};
4107
4108static struct hda_input_mux ad1984a_thinkpad_capture_source = {
4109 .num_items = 3,
4110 .items = {
4111 { "Mic", 0x0 },
4112 { "Internal Mic", 0x5 },
4113 { "Mix", 0x3 },
4114 },
4115};
4116
4117/* mute internal speaker if HP is plugged */
4118static void ad1984a_thinkpad_automute(struct hda_codec *codec)
4119{
4120 unsigned int present;
4121
Takashi Iwaid56757a2009-11-18 08:00:14 +01004122 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01004123 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
4124 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4125}
4126
4127/* unsolicited event for HP jack sensing */
4128static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
4129 unsigned int res)
4130{
4131 if ((res >> 26) != AD1884A_HP_EVENT)
4132 return;
4133 ad1984a_thinkpad_automute(codec);
4134}
4135
4136/* initialize jack-sensing, too */
4137static int ad1984a_thinkpad_init(struct hda_codec *codec)
4138{
4139 ad198x_init(codec);
4140 ad1984a_thinkpad_automute(codec);
4141 return 0;
4142}
4143
4144/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004145 * HP Touchsmart
4146 * port-A (0x11) - front hp-out
4147 * port-B (0x14) - unused
4148 * port-C (0x15) - unused
4149 * port-D (0x12) - rear line out
4150 * port-E (0x1c) - front mic-in
4151 * port-F (0x16) - Internal speakers
4152 * digital-mic (0x17) - Internal mic
4153 */
4154
4155static struct hda_verb ad1984a_touchsmart_verbs[] = {
4156 /* DACs; unmute as default */
4157 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4158 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4159 /* Port-A (HP) mixer - route only from analog mixer */
4160 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4161 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4162 /* Port-A pin */
4163 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4164 /* Port-A (HP) pin - always unmuted */
4165 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4166 /* Port-E (int speaker) mixer - route only from analog mixer */
4167 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4168 /* Port-E pin */
4169 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4170 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4171 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4172 /* Port-F (int speaker) mixer - route only from analog mixer */
4173 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4174 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4175 /* Port-F pin */
4176 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4177 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4178 /* Analog mixer; mute as default */
4179 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4180 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4181 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4182 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4183 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4184 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4185 /* Analog Mix output amp */
4186 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4187 /* capture sources */
4188 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4189 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4190 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4191 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4192 /* unsolicited event for pin-sense */
4193 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4194 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4195 /* allow to touch GPIO1 (for mute control) */
4196 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4197 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4198 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4199 /* internal mic - dmic */
4200 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4201 /* set magic COEFs for dmic */
4202 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4203 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4204 { } /* end */
4205};
4206
4207static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
4208 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4209/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4210 {
4211 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004212 .subdevice = HDA_SUBDEV_AMP_FLAG,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004213 .name = "Master Playback Switch",
4214 .info = snd_hda_mixer_amp_switch_info,
4215 .get = snd_hda_mixer_amp_switch_get,
4216 .put = ad1884a_mobile_master_sw_put,
4217 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4218 },
4219 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4220 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4221 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4222 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4223 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
4224 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4225 { } /* end */
4226};
4227
4228/* switch to external mic if plugged */
4229static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4230{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004231 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004232 snd_hda_codec_write(codec, 0x0c, 0,
4233 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004234 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004235 snd_hda_codec_write(codec, 0x0c, 0,
4236 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004237}
4238
4239
4240/* unsolicited event for HP jack sensing */
4241static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4242 unsigned int res)
4243{
4244 switch (res >> 26) {
4245 case AD1884A_HP_EVENT:
4246 ad1884a_hp_automute(codec);
4247 break;
4248 case AD1884A_MIC_EVENT:
4249 ad1984a_touchsmart_automic(codec);
4250 break;
4251 }
4252}
4253
4254/* initialize jack-sensing, too */
4255static int ad1984a_touchsmart_init(struct hda_codec *codec)
4256{
4257 ad198x_init(codec);
4258 ad1884a_hp_automute(codec);
4259 ad1984a_touchsmart_automic(codec);
4260 return 0;
4261}
4262
4263
4264/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004265 */
4266
4267enum {
4268 AD1884A_DESKTOP,
4269 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004270 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004271 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004272 AD1984A_TOUCHSMART,
Takashi Iwaic5059252008-02-16 09:43:56 +01004273 AD1884A_MODELS
4274};
4275
4276static const char *ad1884a_models[AD1884A_MODELS] = {
4277 [AD1884A_DESKTOP] = "desktop",
4278 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004279 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004280 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004281 [AD1984A_TOUCHSMART] = "touchsmart",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004282};
4283
4284static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
4285 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004286 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004287 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004288 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004289 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004290 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4291 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004292 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004293 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004294 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004295 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004296};
4297
4298static int patch_ad1884a(struct hda_codec *codec)
4299{
4300 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004301 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004302
4303 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4304 if (spec == NULL)
4305 return -ENOMEM;
4306
Takashi Iwaic5059252008-02-16 09:43:56 +01004307 codec->spec = spec;
4308
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004309 err = snd_hda_attach_beep_device(codec, 0x10);
4310 if (err < 0) {
4311 ad198x_free(codec);
4312 return err;
4313 }
4314 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4315
Takashi Iwaic5059252008-02-16 09:43:56 +01004316 spec->multiout.max_channels = 2;
4317 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4318 spec->multiout.dac_nids = ad1884a_dac_nids;
4319 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4320 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4321 spec->adc_nids = ad1884a_adc_nids;
4322 spec->capsrc_nids = ad1884a_capsrc_nids;
4323 spec->input_mux = &ad1884a_capture_source;
4324 spec->num_mixers = 1;
4325 spec->mixers[0] = ad1884a_base_mixers;
4326 spec->num_init_verbs = 1;
4327 spec->init_verbs[0] = ad1884a_init_verbs;
4328 spec->spdif_route = 0;
4329#ifdef CONFIG_SND_HDA_POWER_SAVE
4330 spec->loopback.amplist = ad1884a_loopbacks;
4331#endif
4332 codec->patch_ops = ad198x_patch_ops;
4333
4334 /* override some parameters */
4335 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004336 ad1884a_models,
4337 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01004338 switch (board_config) {
4339 case AD1884A_LAPTOP:
4340 spec->mixers[0] = ad1884a_laptop_mixers;
4341 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4342 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004343 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4344 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004345 /* set the upper-limit for mixer amp to 0dB for avoiding the
4346 * possible damage by overloading
4347 */
4348 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4349 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4350 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4351 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4352 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004353 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004354 case AD1884A_MOBILE:
4355 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004356 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004357 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004358 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4359 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004360 /* set the upper-limit for mixer amp to 0dB for avoiding the
4361 * possible damage by overloading
4362 */
4363 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4364 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4365 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4366 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4367 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004368 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004369 case AD1884A_THINKPAD:
4370 spec->mixers[0] = ad1984a_thinkpad_mixers;
4371 spec->init_verbs[spec->num_init_verbs++] =
4372 ad1984a_thinkpad_verbs;
4373 spec->multiout.dig_out_nid = 0;
4374 spec->input_mux = &ad1984a_thinkpad_capture_source;
4375 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4376 codec->patch_ops.init = ad1984a_thinkpad_init;
4377 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004378 case AD1984A_TOUCHSMART:
4379 spec->mixers[0] = ad1984a_touchsmart_mixers;
4380 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4381 spec->multiout.dig_out_nid = 0;
4382 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4383 codec->patch_ops.init = ad1984a_touchsmart_init;
4384 /* set the upper-limit for mixer amp to 0dB for avoiding the
4385 * possible damage by overloading
4386 */
4387 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4388 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4389 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4390 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4391 (1 << AC_AMPCAP_MUTE_SHIFT));
4392 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004393 }
4394
4395 return 0;
4396}
4397
4398
4399/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004400 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004401 *
4402 * port-A - front hp-out
4403 * port-B - front mic-in
4404 * port-C - rear line-in, shared surr-out (3stack)
4405 * port-D - rear line-out
4406 * port-E - rear mic-in, shared clfe-out (3stack)
4407 * port-F - rear surr-out (6stack)
4408 * port-G - rear clfe-out (6stack)
4409 */
4410
4411static hda_nid_t ad1882_dac_nids[3] = {
4412 0x04, 0x03, 0x05
4413};
4414
4415static hda_nid_t ad1882_adc_nids[2] = {
4416 0x08, 0x09,
4417};
4418
4419static hda_nid_t ad1882_capsrc_nids[2] = {
4420 0x0c, 0x0d,
4421};
4422
4423#define AD1882_SPDIF_OUT 0x02
4424
4425/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
4426static struct hda_input_mux ad1882_capture_source = {
4427 .num_items = 5,
4428 .items = {
4429 { "Front Mic", 0x1 },
4430 { "Mic", 0x4 },
4431 { "Line", 0x2 },
4432 { "CD", 0x3 },
4433 { "Mix", 0x7 },
4434 },
4435};
4436
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004437/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
4438static struct hda_input_mux ad1882a_capture_source = {
4439 .num_items = 5,
4440 .items = {
4441 { "Front Mic", 0x1 },
4442 { "Mic", 0x4},
4443 { "Line", 0x2 },
4444 { "Digital Mic", 0x06 },
4445 { "Mix", 0x7 },
4446 },
4447};
4448
Takashi Iwai0ac85512007-06-20 15:46:13 +02004449static struct snd_kcontrol_new ad1882_base_mixers[] = {
4450 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4451 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4452 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4453 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4454 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4455 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4456 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4457 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004458
Takashi Iwai0ac85512007-06-20 15:46:13 +02004459 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
4460 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
4461 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
4462 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4463 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4464 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4465 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4466 {
4467 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4468 /* The multiple "Capture Source" controls confuse alsamixer
4469 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004470 */
4471 /* .name = "Capture Source", */
4472 .name = "Input Source",
4473 .count = 2,
4474 .info = ad198x_mux_enum_info,
4475 .get = ad198x_mux_enum_get,
4476 .put = ad198x_mux_enum_put,
4477 },
4478 /* SPDIF controls */
4479 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4480 {
4481 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4482 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4483 /* identical with ad1983 */
4484 .info = ad1983_spdif_route_info,
4485 .get = ad1983_spdif_route_get,
4486 .put = ad1983_spdif_route_put,
4487 },
4488 { } /* end */
4489};
4490
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004491static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
4492 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4493 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4494 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4495 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4496 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4497 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4498 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4499 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004500 { } /* end */
4501};
4502
4503static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
4504 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4505 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4506 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4507 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4508 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4509 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4510 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4511 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004512 HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
4513 { } /* end */
4514};
4515
Takashi Iwai0ac85512007-06-20 15:46:13 +02004516static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
4517 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4518 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4519 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4520 {
4521 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4522 .name = "Channel Mode",
4523 .info = ad198x_ch_mode_info,
4524 .get = ad198x_ch_mode_get,
4525 .put = ad198x_ch_mode_put,
4526 },
4527 { } /* end */
4528};
4529
4530static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
4531 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4532 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4533 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4534 { } /* end */
4535};
4536
4537static struct hda_verb ad1882_ch2_init[] = {
4538 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4539 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4540 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4541 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4542 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4543 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4544 { } /* end */
4545};
4546
4547static struct hda_verb ad1882_ch4_init[] = {
4548 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4549 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4550 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4551 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4552 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4553 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4554 { } /* end */
4555};
4556
4557static struct hda_verb ad1882_ch6_init[] = {
4558 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4559 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4560 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4561 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4562 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4563 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4564 { } /* end */
4565};
4566
4567static struct hda_channel_mode ad1882_modes[3] = {
4568 { 2, ad1882_ch2_init },
4569 { 4, ad1882_ch4_init },
4570 { 6, ad1882_ch6_init },
4571};
4572
4573/*
4574 * initialization verbs
4575 */
4576static struct hda_verb ad1882_init_verbs[] = {
4577 /* DACs; mute as default */
4578 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4579 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4580 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4581 /* Port-A (HP) mixer */
4582 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4583 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4584 /* Port-A pin */
4585 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4586 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4587 /* HP selector - select DAC2 */
4588 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4589 /* Port-D (Line-out) mixer */
4590 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4591 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4592 /* Port-D pin */
4593 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4594 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4595 /* Mono-out mixer */
4596 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4597 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4598 /* Mono-out pin */
4599 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4600 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4601 /* Port-B (front mic) pin */
4602 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4603 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4604 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4605 /* Port-C (line-in) pin */
4606 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4607 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4608 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4609 /* Port-C mixer - mute as input */
4610 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4611 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4612 /* Port-E (mic-in) pin */
4613 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4614 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4615 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4616 /* Port-E mixer - mute as input */
4617 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4618 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4619 /* Port-F (surround) */
4620 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4621 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4622 /* Port-G (CLFE) */
4623 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4624 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4625 /* Analog mixer; mute as default */
4626 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4627 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4628 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4629 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4630 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4631 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4632 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4633 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4634 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4635 /* Analog Mix output amp */
4636 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4637 /* SPDIF output selector */
4638 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4639 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4640 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4641 { } /* end */
4642};
4643
Takashi Iwaicb53c622007-08-10 17:21:45 +02004644#ifdef CONFIG_SND_HDA_POWER_SAVE
4645static struct hda_amp_list ad1882_loopbacks[] = {
4646 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4647 { 0x20, HDA_INPUT, 1 }, /* Mic */
4648 { 0x20, HDA_INPUT, 4 }, /* Line */
4649 { 0x20, HDA_INPUT, 6 }, /* CD */
4650 { } /* end */
4651};
4652#endif
4653
Takashi Iwai0ac85512007-06-20 15:46:13 +02004654/* models */
4655enum {
4656 AD1882_3STACK,
4657 AD1882_6STACK,
4658 AD1882_MODELS
4659};
4660
4661static const char *ad1882_models[AD1986A_MODELS] = {
4662 [AD1882_3STACK] = "3stack",
4663 [AD1882_6STACK] = "6stack",
4664};
4665
4666
4667static int patch_ad1882(struct hda_codec *codec)
4668{
4669 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004670 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004671
4672 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4673 if (spec == NULL)
4674 return -ENOMEM;
4675
Takashi Iwai0ac85512007-06-20 15:46:13 +02004676 codec->spec = spec;
4677
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004678 err = snd_hda_attach_beep_device(codec, 0x10);
4679 if (err < 0) {
4680 ad198x_free(codec);
4681 return err;
4682 }
4683 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4684
Takashi Iwai0ac85512007-06-20 15:46:13 +02004685 spec->multiout.max_channels = 6;
4686 spec->multiout.num_dacs = 3;
4687 spec->multiout.dac_nids = ad1882_dac_nids;
4688 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
4689 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
4690 spec->adc_nids = ad1882_adc_nids;
4691 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004692 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004693 spec->input_mux = &ad1882_capture_source;
4694 else
4695 spec->input_mux = &ad1882a_capture_source;
4696 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004697 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004698 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004699 spec->mixers[1] = ad1882_loopback_mixers;
4700 else
4701 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004702 spec->num_init_verbs = 1;
4703 spec->init_verbs[0] = ad1882_init_verbs;
4704 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02004705#ifdef CONFIG_SND_HDA_POWER_SAVE
4706 spec->loopback.amplist = ad1882_loopbacks;
4707#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01004708 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004709
4710 codec->patch_ops = ad198x_patch_ops;
4711
4712 /* override some parameters */
4713 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
4714 ad1882_models, NULL);
4715 switch (board_config) {
4716 default:
4717 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004718 spec->num_mixers = 3;
4719 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004720 spec->channel_mode = ad1882_modes;
4721 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
4722 spec->need_dac_fix = 1;
4723 spec->multiout.max_channels = 2;
4724 spec->multiout.num_dacs = 1;
4725 break;
4726 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004727 spec->num_mixers = 3;
4728 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004729 break;
4730 }
4731 return 0;
4732}
4733
4734
4735/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004736 * patch entries
4737 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004738static struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004739 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02004740 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004741 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004742 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004743 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
4744 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02004745 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
4746 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004747 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004748 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01004749 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02004750 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004751 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02004752 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
4753 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004754 {} /* terminator */
4755};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004756
4757MODULE_ALIAS("snd-hda-codec-id:11d4*");
4758
4759MODULE_LICENSE("GPL");
4760MODULE_DESCRIPTION("Analog Devices HD-audio codec");
4761
4762static struct hda_codec_preset_list analog_list = {
4763 .preset = snd_hda_preset_analog,
4764 .owner = THIS_MODULE,
4765};
4766
4767static int __init patch_analog_init(void)
4768{
4769 return snd_hda_add_codec_preset(&analog_list);
4770}
4771
4772static void __exit patch_analog_exit(void)
4773{
4774 snd_hda_delete_codec_preset(&analog_list);
4775}
4776
4777module_init(patch_analog_init)
4778module_exit(patch_analog_exit)