blob: e9fdfc4b1c57bd92a6cffd77abc9d0577a361a4a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Takashi Iwai0ac85512007-06-20 15:46:13 +02002 * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
3 * AD1986A, AD1988
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
Takashi Iwai2bac6472007-05-18 18:21:41 +02005 * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 *
7 * This driver is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This driver is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/init.h>
23#include <linux/delay.h>
24#include <linux/slab.h>
25#include <linux/pci.h>
Ingo Molnar62932df2006-01-16 16:34:20 +010026
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <sound/core.h>
28#include "hda_codec.h"
29#include "hda_local.h"
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010030#include "hda_beep.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020032struct ad198x_spec {
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +010033 struct snd_kcontrol_new *mixers[5];
Takashi Iwai985be542005-11-02 18:26:49 +010034 int num_mixers;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010035 unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
Takashi Iwaid32410b12005-11-24 16:06:23 +010036 const struct hda_verb *init_verbs[5]; /* initialization verbs
Takashi Iwai985be542005-11-02 18:26:49 +010037 * don't forget NULL termination!
38 */
39 unsigned int num_init_verbs;
40
41 /* playback */
42 struct hda_multi_out multiout; /* playback set-up
43 * max_channels, dacs must be set
44 * dig_out_nid and hp_nid are optional
45 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +010046 unsigned int cur_eapd;
Takashi Iwai2125cad2006-03-27 12:52:22 +020047 unsigned int need_dac_fix;
Takashi Iwai985be542005-11-02 18:26:49 +010048
49 /* capture */
50 unsigned int num_adc_nids;
51 hda_nid_t *adc_nids;
52 hda_nid_t dig_in_nid; /* digital-in NID; optional */
53
54 /* capture source */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020055 const struct hda_input_mux *input_mux;
Takashi Iwai2e5b9562005-11-21 16:36:15 +010056 hda_nid_t *capsrc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010057 unsigned int cur_mux[3];
58
59 /* channel model */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +010060 const struct hda_channel_mode *channel_mode;
Takashi Iwai985be542005-11-02 18:26:49 +010061 int num_channel_mode;
62
63 /* PCM information */
Takashi Iwai2bac6472007-05-18 18:21:41 +020064 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
Takashi Iwai985be542005-11-02 18:26:49 +010065
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020066 unsigned int spdif_route;
Takashi Iwaid32410b12005-11-24 16:06:23 +010067
68 /* dynamic controls, init_verbs and input_mux */
69 struct auto_pin_cfg autocfg;
Takashi Iwai603c4012008-07-30 15:01:44 +020070 struct snd_array kctls;
Takashi Iwaid32410b12005-11-24 16:06:23 +010071 struct hda_input_mux private_imux;
Takashi Iwai41923e42007-10-22 17:20:10 +020072 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
Takashi Iwaicb53c622007-08-10 17:21:45 +020073
Takashi Iwai8ab78c72007-09-06 14:29:53 +020074 unsigned int jack_present :1;
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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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 Iwaic8b6bf9b2005-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}
Daniel T Chenea52bf22009-12-27 18:48:29 -0500522#endif
523
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200524static struct hda_codec_ops ad198x_patch_ops = {
525 .build_controls = ad198x_build_controls,
526 .build_pcms = ad198x_build_pcms,
527 .init = ad198x_init,
528 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200529#ifdef CONFIG_SND_HDA_POWER_SAVE
530 .check_power_status = ad198x_check_power_status,
531#endif
Daniel T Chenea52bf22009-12-27 18:48:29 -0500532#ifdef SND_HDA_NEEDS_RESUME
533 .suspend = ad198x_suspend,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500534#endif
535 .reboot_notify = ad198x_shutup,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200536};
537
538
539/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100540 * EAPD control
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100541 * the private value = nid
Takashi Iwai18a815d2006-03-01 19:54:39 +0100542 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200543#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100544
545static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
546 struct snd_ctl_elem_value *ucontrol)
547{
548 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
549 struct ad198x_spec *spec = codec->spec;
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100550 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100551 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
552 else
553 ucontrol->value.integer.value[0] = spec->cur_eapd;
554 return 0;
555}
556
557static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
558 struct snd_ctl_elem_value *ucontrol)
559{
560 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
561 struct ad198x_spec *spec = codec->spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +0100562 hda_nid_t nid = kcontrol->private_value & 0xff;
563 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100564 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100565 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100566 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200567 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100568 return 0;
569 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200570 snd_hda_codec_write_cache(codec, nid,
571 0, AC_VERB_SET_EAPD_BTLENABLE,
572 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100573 return 1;
574}
575
Takashi Iwai9230d212006-03-13 13:49:49 +0100576static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
577 struct snd_ctl_elem_info *uinfo);
578static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
579 struct snd_ctl_elem_value *ucontrol);
580static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
581 struct snd_ctl_elem_value *ucontrol);
582
583
Takashi Iwai18a815d2006-03-01 19:54:39 +0100584/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200585 * AD1986A specific
586 */
587
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588#define AD1986A_SPDIF_OUT 0x02
589#define AD1986A_FRONT_DAC 0x03
590#define AD1986A_SURR_DAC 0x04
591#define AD1986A_CLFE_DAC 0x05
592#define AD1986A_ADC 0x06
593
594static hda_nid_t ad1986a_dac_nids[3] = {
595 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
596};
Takashi Iwai985be542005-11-02 18:26:49 +0100597static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +0100598static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599
600static struct hda_input_mux ad1986a_capture_source = {
601 .num_items = 7,
602 .items = {
603 { "Mic", 0x0 },
604 { "CD", 0x1 },
605 { "Aux", 0x3 },
606 { "Line", 0x4 },
607 { "Mix", 0x5 },
608 { "Mono", 0x6 },
609 { "Phone", 0x7 },
610 },
611};
612
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
Takashi Iwai532d5382007-07-27 19:02:40 +0200614static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
615 .ops = &snd_hda_bind_vol,
616 .values = {
617 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
618 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
619 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
620 0
621 },
622};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623
Takashi Iwai532d5382007-07-27 19:02:40 +0200624static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
625 .ops = &snd_hda_bind_sw,
626 .values = {
627 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
628 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
629 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
630 0
631 },
632};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633
634/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 * mixers
636 */
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100637static struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200638 /*
639 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
640 */
641 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
642 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
644 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
645 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
646 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
647 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
648 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
649 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
650 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
651 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
652 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
653 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
654 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
655 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
656 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
657 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
658 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
659 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
660 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100661 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
663 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
664 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
665 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
666 {
667 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
668 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200669 .info = ad198x_mux_enum_info,
670 .get = ad198x_mux_enum_get,
671 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 },
673 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
674 { } /* end */
675};
676
Takashi Iwai9230d212006-03-13 13:49:49 +0100677/* additional mixers for 3stack mode */
678static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
679 {
680 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
681 .name = "Channel Mode",
682 .info = ad198x_ch_mode_info,
683 .get = ad198x_ch_mode_get,
684 .put = ad198x_ch_mode_put,
685 },
686 { } /* end */
687};
688
689/* laptop model - 2ch only */
690static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
691
Takashi Iwai20a45e82007-08-15 22:20:45 +0200692/* master controls both pins 0x1a and 0x1b */
693static struct hda_bind_ctls ad1986a_laptop_master_vol = {
694 .ops = &snd_hda_bind_vol,
695 .values = {
696 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
697 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
698 0,
699 },
700};
701
702static struct hda_bind_ctls ad1986a_laptop_master_sw = {
703 .ops = &snd_hda_bind_sw,
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
Takashi Iwai9230d212006-03-13 13:49:49 +0100711static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
712 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
713 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200714 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
715 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100716 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
717 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
718 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
719 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
720 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
721 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
722 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
723 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100724 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100725 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100726 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
727 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
728 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
729 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
730 {
731 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
732 .name = "Capture Source",
733 .info = ad198x_mux_enum_info,
734 .get = ad198x_mux_enum_get,
735 .put = ad198x_mux_enum_put,
736 },
737 { } /* end */
738};
739
Takashi Iwai825aa972006-03-17 10:50:49 +0100740/* laptop-eapd model - 2ch only */
741
Takashi Iwai825aa972006-03-17 10:50:49 +0100742static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
743 .num_items = 3,
744 .items = {
745 { "Mic", 0x0 },
746 { "Internal Mic", 0x4 },
747 { "Mix", 0x5 },
748 },
749};
750
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100751static struct hda_input_mux ad1986a_automic_capture_source = {
752 .num_items = 2,
753 .items = {
754 { "Mic", 0x0 },
755 { "Mix", 0x5 },
756 },
757};
758
Takashi Iwai16d11a82009-06-24 14:07:53 +0200759static struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200760 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
761 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai16d11a82009-06-24 14:07:53 +0200762 { } /* end */
763};
764
765static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai825aa972006-03-17 10:50:49 +0100766 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
767 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100768 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
769 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
770 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
771 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
772 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
773 {
774 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
775 .name = "Capture Source",
776 .info = ad198x_mux_enum_info,
777 .get = ad198x_mux_enum_get,
778 .put = ad198x_mux_enum_put,
779 },
780 {
781 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
782 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100783 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwai1725b822008-11-21 02:25:48 +0100784 .info = ad198x_eapd_info,
785 .get = ad198x_eapd_get,
786 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100787 .private_value = 0x1b, /* port-D */
Takashi Iwai1725b822008-11-21 02:25:48 +0100788 },
789 { } /* end */
790};
791
Takashi Iwai16d11a82009-06-24 14:07:53 +0200792static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
793 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
794 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
Takashi Iwai825aa972006-03-17 10:50:49 +0100795 { } /* end */
796};
797
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100798/* re-connect the mic boost input according to the jack sensing */
799static void ad1986a_automic(struct hda_codec *codec)
800{
801 unsigned int present;
Takashi Iwaid56757a2009-11-18 08:00:14 +0100802 present = snd_hda_jack_detect(codec, 0x1f);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100803 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
804 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
Takashi Iwaid56757a2009-11-18 08:00:14 +0100805 present ? 0 : 2);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100806}
807
808#define AD1986A_MIC_EVENT 0x36
809
810static void ad1986a_automic_unsol_event(struct hda_codec *codec,
811 unsigned int res)
812{
813 if ((res >> 26) != AD1986A_MIC_EVENT)
814 return;
815 ad1986a_automic(codec);
816}
817
818static int ad1986a_automic_init(struct hda_codec *codec)
819{
820 ad198x_init(codec);
821 ad1986a_automic(codec);
822 return 0;
823}
824
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200825/* laptop-automute - 2ch only */
826
827static void ad1986a_update_hp(struct hda_codec *codec)
828{
829 struct ad198x_spec *spec = codec->spec;
830 unsigned int mute;
831
832 if (spec->jack_present)
833 mute = HDA_AMP_MUTE; /* mute internal speaker */
834 else
835 /* unmute internal speaker if necessary */
836 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
837 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
838 HDA_AMP_MUTE, mute);
839}
840
841static void ad1986a_hp_automute(struct hda_codec *codec)
842{
843 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200844
Takashi Iwaid56757a2009-11-18 08:00:14 +0100845 spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
Takashi Iwai03c405a2009-06-24 14:10:15 +0200846 if (spec->inv_jack_detect)
847 spec->jack_present = !spec->jack_present;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200848 ad1986a_update_hp(codec);
849}
850
851#define AD1986A_HP_EVENT 0x37
852
853static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
854{
855 if ((res >> 26) != AD1986A_HP_EVENT)
856 return;
857 ad1986a_hp_automute(codec);
858}
859
860static int ad1986a_hp_init(struct hda_codec *codec)
861{
862 ad198x_init(codec);
863 ad1986a_hp_automute(codec);
864 return 0;
865}
866
867/* bind hp and internal speaker mute (with plug check) */
868static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
869 struct snd_ctl_elem_value *ucontrol)
870{
871 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
872 long *valp = ucontrol->value.integer.value;
873 int change;
874
875 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
876 HDA_AMP_MUTE,
877 valp[0] ? 0 : HDA_AMP_MUTE);
878 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
879 HDA_AMP_MUTE,
880 valp[1] ? 0 : HDA_AMP_MUTE);
881 if (change)
882 ad1986a_update_hp(codec);
883 return change;
884}
885
Takashi Iwai16d11a82009-06-24 14:07:53 +0200886static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200887 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
888 {
889 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
890 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100891 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200892 .info = snd_hda_mixer_amp_switch_info,
893 .get = snd_hda_mixer_amp_switch_get,
894 .put = ad1986a_hp_master_sw_put,
895 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
896 },
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200897 { } /* end */
898};
899
Takashi Iwai16d11a82009-06-24 14:07:53 +0200900
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901/*
902 * initialization verbs
903 */
904static struct hda_verb ad1986a_init_verbs[] = {
905 /* Front, Surround, CLFE DAC; mute as default */
906 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
907 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
908 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
909 /* Downmix - off */
910 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
911 /* HP, Line-Out, Surround, CLFE selectors */
912 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
913 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
914 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
915 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
916 /* Mono selector */
917 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
918 /* Mic selector: Mic 1/2 pin */
919 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
920 /* Line-in selector: Line-in */
921 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
922 /* Mic 1/2 swap */
923 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
924 /* Record selector: mic */
925 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
926 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
927 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
928 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
929 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
930 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
931 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
932 /* PC beep */
933 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
934 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
935 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
936 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
937 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
938 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
939 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200940 /* HP Pin */
941 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
942 /* Front, Surround, CLFE Pins */
943 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
944 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
945 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
946 /* Mono Pin */
947 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
948 /* Mic Pin */
949 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
950 /* Line, Aux, CD, Beep-In Pin */
951 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
952 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
953 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
954 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
955 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 { } /* end */
957};
958
Takashi Iwai9230d212006-03-13 13:49:49 +0100959static struct hda_verb ad1986a_ch2_init[] = {
960 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200961 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
962 /* Line-in selectors */
963 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100964 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200965 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
966 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
967 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100968 { } /* end */
969};
970
971static struct hda_verb ad1986a_ch4_init[] = {
972 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200973 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
974 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100975 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200976 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
977 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100978 { } /* end */
979};
980
981static struct hda_verb ad1986a_ch6_init[] = {
982 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200983 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
984 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100985 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200986 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
987 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100988 { } /* end */
989};
990
991static struct hda_channel_mode ad1986a_modes[3] = {
992 { 2, ad1986a_ch2_init },
993 { 4, ad1986a_ch4_init },
994 { 6, ad1986a_ch6_init },
995};
996
Takashi Iwai825aa972006-03-17 10:50:49 +0100997/* eapd initialization */
998static struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +0100999 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa972006-03-17 10:50:49 +01001000 {}
1001};
1002
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001003static struct hda_verb ad1986a_automic_verbs[] = {
1004 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1005 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1006 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
1007 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
1008 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
1009 {}
1010};
1011
Tobin Davisf36090f2007-01-08 11:07:12 +01001012/* Ultra initialization */
1013static struct hda_verb ad1986a_ultra_init[] = {
1014 /* eapd initialization */
1015 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
1016 /* CLFE -> Mic in */
1017 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
1018 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1019 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
1020 { } /* end */
1021};
1022
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001023/* pin sensing on HP jack */
1024static struct hda_verb ad1986a_hp_init_verbs[] = {
1025 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
1026 {}
1027};
1028
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001029static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
1030 unsigned int res)
1031{
1032 switch (res >> 26) {
1033 case AD1986A_HP_EVENT:
1034 ad1986a_hp_automute(codec);
1035 break;
1036 case AD1986A_MIC_EVENT:
1037 ad1986a_automic(codec);
1038 break;
1039 }
1040}
1041
1042static int ad1986a_samsung_p50_init(struct hda_codec *codec)
1043{
1044 ad198x_init(codec);
1045 ad1986a_hp_automute(codec);
1046 ad1986a_automic(codec);
1047 return 0;
1048}
1049
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001050
Takashi Iwai9230d212006-03-13 13:49:49 +01001051/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001052enum {
1053 AD1986A_6STACK,
1054 AD1986A_3STACK,
1055 AD1986A_LAPTOP,
1056 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001057 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +01001058 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +01001059 AD1986A_SAMSUNG,
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001060 AD1986A_SAMSUNG_P50,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001061 AD1986A_MODELS
1062};
Takashi Iwai9230d212006-03-13 13:49:49 +01001063
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001064static const char *ad1986a_models[AD1986A_MODELS] = {
1065 [AD1986A_6STACK] = "6stack",
1066 [AD1986A_3STACK] = "3stack",
1067 [AD1986A_LAPTOP] = "laptop",
1068 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001069 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +01001070 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +01001071 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001072 [AD1986A_SAMSUNG_P50] = "samsung-p50",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001073};
1074
1075static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
1076 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001077 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001078 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001079 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001080 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1081 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1082 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1083 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001084 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001085 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001086 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1087 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1088 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1089 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1090 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001091 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Daniel T Chenba579eb2010-02-20 11:16:30 -05001092 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001093 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001094 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001095 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
Tobin Davisf36090f2007-01-08 11:07:12 +01001096 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001097 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001098 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001099 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001100 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001101 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001102 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001103 {}
1104};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105
Takashi Iwaicb53c622007-08-10 17:21:45 +02001106#ifdef CONFIG_SND_HDA_POWER_SAVE
1107static struct hda_amp_list ad1986a_loopbacks[] = {
1108 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1109 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1110 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1111 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1112 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1113 { } /* end */
1114};
1115#endif
1116
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001117static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1118{
Takashi Iwai2f334f92009-02-20 14:37:42 +01001119 unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001120 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1121}
1122
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123static int patch_ad1986a(struct hda_codec *codec)
1124{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001125 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001126 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001128 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 if (spec == NULL)
1130 return -ENOMEM;
1131
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 codec->spec = spec;
1133
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001134 err = snd_hda_attach_beep_device(codec, 0x19);
1135 if (err < 0) {
1136 ad198x_free(codec);
1137 return err;
1138 }
1139 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1140
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 spec->multiout.max_channels = 6;
1142 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1143 spec->multiout.dac_nids = ad1986a_dac_nids;
1144 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001145 spec->num_adc_nids = 1;
1146 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001147 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001148 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001149 spec->num_mixers = 1;
1150 spec->mixers[0] = ad1986a_mixers;
1151 spec->num_init_verbs = 1;
1152 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001153#ifdef CONFIG_SND_HDA_POWER_SAVE
1154 spec->loopback.amplist = ad1986a_loopbacks;
1155#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001156 spec->vmaster_nid = 0x1b;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01001157 spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001159 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160
Takashi Iwai9230d212006-03-13 13:49:49 +01001161 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001162 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1163 ad1986a_models,
1164 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001165 switch (board_config) {
1166 case AD1986A_3STACK:
1167 spec->num_mixers = 2;
1168 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001169 spec->num_init_verbs = 2;
1170 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001171 spec->channel_mode = ad1986a_modes;
1172 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001173 spec->need_dac_fix = 1;
1174 spec->multiout.max_channels = 2;
1175 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001176 break;
1177 case AD1986A_LAPTOP:
1178 spec->mixers[0] = ad1986a_laptop_mixers;
1179 spec->multiout.max_channels = 2;
1180 spec->multiout.num_dacs = 1;
1181 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1182 break;
Takashi Iwai825aa972006-03-17 10:50:49 +01001183 case AD1986A_LAPTOP_EAPD:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001184 spec->num_mixers = 3;
1185 spec->mixers[0] = ad1986a_laptop_master_mixers;
1186 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1187 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001188 spec->num_init_verbs = 2;
1189 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1190 spec->multiout.max_channels = 2;
1191 spec->multiout.num_dacs = 1;
1192 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1193 if (!is_jack_available(codec, 0x25))
1194 spec->multiout.dig_out_nid = 0;
1195 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1196 break;
1197 case AD1986A_SAMSUNG:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001198 spec->num_mixers = 2;
1199 spec->mixers[0] = ad1986a_laptop_master_mixers;
1200 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001201 spec->num_init_verbs = 3;
Takashi Iwai825aa972006-03-17 10:50:49 +01001202 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001203 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa972006-03-17 10:50:49 +01001204 spec->multiout.max_channels = 2;
1205 spec->multiout.num_dacs = 1;
1206 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001207 if (!is_jack_available(codec, 0x25))
1208 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001209 spec->input_mux = &ad1986a_automic_capture_source;
1210 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1211 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa972006-03-17 10:50:49 +01001212 break;
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001213 case AD1986A_SAMSUNG_P50:
1214 spec->num_mixers = 2;
1215 spec->mixers[0] = ad1986a_automute_master_mixers;
1216 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1217 spec->num_init_verbs = 4;
1218 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1219 spec->init_verbs[2] = ad1986a_automic_verbs;
1220 spec->init_verbs[3] = ad1986a_hp_init_verbs;
1221 spec->multiout.max_channels = 2;
1222 spec->multiout.num_dacs = 1;
1223 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1224 if (!is_jack_available(codec, 0x25))
1225 spec->multiout.dig_out_nid = 0;
1226 spec->input_mux = &ad1986a_automic_capture_source;
1227 codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
1228 codec->patch_ops.init = ad1986a_samsung_p50_init;
1229 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001230 case AD1986A_LAPTOP_AUTOMUTE:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001231 spec->num_mixers = 3;
1232 spec->mixers[0] = ad1986a_automute_master_mixers;
1233 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1234 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001235 spec->num_init_verbs = 3;
1236 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1237 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1238 spec->multiout.max_channels = 2;
1239 spec->multiout.num_dacs = 1;
1240 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001241 if (!is_jack_available(codec, 0x25))
1242 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001243 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1244 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1245 codec->patch_ops.init = ad1986a_hp_init;
Takashi Iwai03c405a2009-06-24 14:10:15 +02001246 /* Lenovo N100 seems to report the reversed bit
1247 * for HP jack-sensing
1248 */
1249 spec->inv_jack_detect = 1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001250 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001251 case AD1986A_ULTRA:
1252 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1253 spec->num_init_verbs = 2;
1254 spec->init_verbs[1] = ad1986a_ultra_init;
1255 spec->multiout.max_channels = 2;
1256 spec->multiout.num_dacs = 1;
1257 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1258 spec->multiout.dig_out_nid = 0;
1259 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001260 }
1261
Takashi Iwaid29240c2007-10-26 12:35:56 +02001262 /* AD1986A has a hardware problem that it can't share a stream
1263 * with multiple output pins. The copy of front to surrounds
1264 * causes noisy or silent outputs at a certain timing, e.g.
1265 * changing the volume.
1266 * So, let's disable the shared stream.
1267 */
1268 spec->multiout.no_share_stream = 1;
1269
Takashi Iwai729d55b2009-12-25 22:49:01 +01001270 codec->no_trigger_sense = 1;
1271
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 return 0;
1273}
1274
1275/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001276 * AD1983 specific
1277 */
1278
1279#define AD1983_SPDIF_OUT 0x02
1280#define AD1983_DAC 0x03
1281#define AD1983_ADC 0x04
1282
1283static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001284static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001285static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001286
1287static struct hda_input_mux ad1983_capture_source = {
1288 .num_items = 4,
1289 .items = {
1290 { "Mic", 0x0 },
1291 { "Line", 0x1 },
1292 { "Mix", 0x2 },
1293 { "Mix Mono", 0x3 },
1294 },
1295};
1296
1297/*
1298 * SPDIF playback route
1299 */
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001300static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001301{
1302 static char *texts[] = { "PCM", "ADC" };
1303
1304 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1305 uinfo->count = 1;
1306 uinfo->value.enumerated.items = 2;
1307 if (uinfo->value.enumerated.item > 1)
1308 uinfo->value.enumerated.item = 1;
1309 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1310 return 0;
1311}
1312
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001313static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001314{
1315 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1316 struct ad198x_spec *spec = codec->spec;
1317
1318 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1319 return 0;
1320}
1321
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001322static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001323{
1324 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1325 struct ad198x_spec *spec = codec->spec;
1326
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001327 if (ucontrol->value.enumerated.item[0] > 1)
1328 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001329 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1330 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001331 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1332 AC_VERB_SET_CONNECT_SEL,
1333 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001334 return 1;
1335 }
1336 return 0;
1337}
1338
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001339static struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001340 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1341 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1342 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1343 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1344 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1345 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1346 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1347 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1348 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1349 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1350 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1351 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001352 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
1353 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1354 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1355 {
1356 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1357 .name = "Capture Source",
1358 .info = ad198x_mux_enum_info,
1359 .get = ad198x_mux_enum_get,
1360 .put = ad198x_mux_enum_put,
1361 },
1362 {
1363 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001364 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001365 .info = ad1983_spdif_route_info,
1366 .get = ad1983_spdif_route_get,
1367 .put = ad1983_spdif_route_put,
1368 },
1369 { } /* end */
1370};
1371
1372static struct hda_verb ad1983_init_verbs[] = {
1373 /* Front, HP, Mono; mute as default */
1374 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1375 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1376 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1377 /* Beep, PCM, Mic, Line-In: mute */
1378 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1379 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1380 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1381 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1382 /* Front, HP selectors; from Mix */
1383 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1384 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1385 /* Mono selector; from Mix */
1386 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1387 /* Mic selector; Mic */
1388 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1389 /* Line-in selector: Line-in */
1390 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1391 /* Mic boost: 0dB */
1392 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1393 /* Record selector: mic */
1394 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1395 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1396 /* SPDIF route: PCM */
1397 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1398 /* Front Pin */
1399 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1400 /* HP Pin */
1401 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1402 /* Mono Pin */
1403 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1404 /* Mic Pin */
1405 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1406 /* Line Pin */
1407 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1408 { } /* end */
1409};
1410
Takashi Iwaicb53c622007-08-10 17:21:45 +02001411#ifdef CONFIG_SND_HDA_POWER_SAVE
1412static struct hda_amp_list ad1983_loopbacks[] = {
1413 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1414 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1415 { } /* end */
1416};
1417#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001418
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001419static int patch_ad1983(struct hda_codec *codec)
1420{
1421 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001422 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001423
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001424 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001425 if (spec == NULL)
1426 return -ENOMEM;
1427
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001428 codec->spec = spec;
1429
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001430 err = snd_hda_attach_beep_device(codec, 0x10);
1431 if (err < 0) {
1432 ad198x_free(codec);
1433 return err;
1434 }
1435 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1436
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001437 spec->multiout.max_channels = 2;
1438 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1439 spec->multiout.dac_nids = ad1983_dac_nids;
1440 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001441 spec->num_adc_nids = 1;
1442 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001443 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001444 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001445 spec->num_mixers = 1;
1446 spec->mixers[0] = ad1983_mixers;
1447 spec->num_init_verbs = 1;
1448 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001449 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001450#ifdef CONFIG_SND_HDA_POWER_SAVE
1451 spec->loopback.amplist = ad1983_loopbacks;
1452#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001453 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001454
1455 codec->patch_ops = ad198x_patch_ops;
1456
Takashi Iwai729d55b2009-12-25 22:49:01 +01001457 codec->no_trigger_sense = 1;
1458
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001459 return 0;
1460}
1461
1462
1463/*
1464 * AD1981 HD specific
1465 */
1466
1467#define AD1981_SPDIF_OUT 0x02
1468#define AD1981_DAC 0x03
1469#define AD1981_ADC 0x04
1470
1471static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001472static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001473static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001474
1475/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
1476static struct hda_input_mux ad1981_capture_source = {
1477 .num_items = 7,
1478 .items = {
1479 { "Front Mic", 0x0 },
1480 { "Line", 0x1 },
1481 { "Mix", 0x2 },
1482 { "Mix Mono", 0x3 },
1483 { "CD", 0x4 },
1484 { "Mic", 0x6 },
1485 { "Aux", 0x7 },
1486 },
1487};
1488
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001489static struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001490 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1491 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1492 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1493 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1494 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1495 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1496 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1497 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1498 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1499 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1500 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1501 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1502 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1503 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1504 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1505 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1506 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1507 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001508 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
1509 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
1510 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1511 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1512 {
1513 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1514 .name = "Capture Source",
1515 .info = ad198x_mux_enum_info,
1516 .get = ad198x_mux_enum_get,
1517 .put = ad198x_mux_enum_put,
1518 },
1519 /* identical with AD1983 */
1520 {
1521 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001522 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001523 .info = ad1983_spdif_route_info,
1524 .get = ad1983_spdif_route_get,
1525 .put = ad1983_spdif_route_put,
1526 },
1527 { } /* end */
1528};
1529
1530static struct hda_verb ad1981_init_verbs[] = {
1531 /* Front, HP, Mono; mute as default */
1532 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1533 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1534 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1535 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1536 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1537 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1538 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1539 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1540 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1541 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1542 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1543 /* Front, HP selectors; from Mix */
1544 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1545 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1546 /* Mono selector; from Mix */
1547 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1548 /* Mic Mixer; select Front Mic */
1549 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1550 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1551 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001552 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1553 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001554 /* Record selector: Front mic */
1555 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1556 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1557 /* SPDIF route: PCM */
1558 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1559 /* Front Pin */
1560 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1561 /* HP Pin */
1562 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1563 /* Mono Pin */
1564 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1565 /* Front & Rear Mic Pins */
1566 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1567 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1568 /* Line Pin */
1569 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1570 /* Digital Beep */
1571 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1572 /* Line-Out as Input: disabled */
1573 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1574 { } /* end */
1575};
1576
Takashi Iwaicb53c622007-08-10 17:21:45 +02001577#ifdef CONFIG_SND_HDA_POWER_SAVE
1578static struct hda_amp_list ad1981_loopbacks[] = {
1579 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1580 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1581 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1582 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1583 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1584 { } /* end */
1585};
1586#endif
1587
Takashi Iwai18a815d2006-03-01 19:54:39 +01001588/*
1589 * Patch for HP nx6320
1590 *
Tobin Davis18768992007-03-12 22:20:51 +01001591 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001592 * speaker output enabled _and_ mute-LED off.
1593 */
1594
1595#define AD1981_HP_EVENT 0x37
1596#define AD1981_MIC_EVENT 0x38
1597
1598static struct hda_verb ad1981_hp_init_verbs[] = {
1599 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1600 /* pin sensing on HP and Mic jacks */
1601 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1602 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1603 {}
1604};
1605
1606/* turn on/off EAPD (+ mute HP) as a master switch */
1607static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1608 struct snd_ctl_elem_value *ucontrol)
1609{
1610 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1611 struct ad198x_spec *spec = codec->spec;
1612
1613 if (! ad198x_eapd_put(kcontrol, ucontrol))
1614 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001615 /* change speaker pin appropriately */
1616 snd_hda_codec_write(codec, 0x05, 0,
1617 AC_VERB_SET_PIN_WIDGET_CONTROL,
1618 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001619 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001620 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1621 HDA_AMP_MUTE,
1622 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001623 return 1;
1624}
1625
1626/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02001627static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
1628 .ops = &snd_hda_bind_vol,
1629 .values = {
1630 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1631 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1632 0
1633 },
1634};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001635
1636/* mute internal speaker if HP is plugged */
1637static void ad1981_hp_automute(struct hda_codec *codec)
1638{
1639 unsigned int present;
1640
Takashi Iwaid56757a2009-11-18 08:00:14 +01001641 present = snd_hda_jack_detect(codec, 0x06);
Takashi Iwai47fd8302007-08-10 17:11:07 +02001642 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1643 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001644}
1645
1646/* toggle input of built-in and mic jack appropriately */
1647static void ad1981_hp_automic(struct hda_codec *codec)
1648{
1649 static struct hda_verb mic_jack_on[] = {
1650 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1651 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1652 {}
1653 };
1654 static struct hda_verb mic_jack_off[] = {
1655 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1656 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1657 {}
1658 };
1659 unsigned int present;
1660
Takashi Iwaid56757a2009-11-18 08:00:14 +01001661 present = snd_hda_jack_detect(codec, 0x08);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001662 if (present)
1663 snd_hda_sequence_write(codec, mic_jack_on);
1664 else
1665 snd_hda_sequence_write(codec, mic_jack_off);
1666}
1667
1668/* unsolicited event for HP jack sensing */
1669static void ad1981_hp_unsol_event(struct hda_codec *codec,
1670 unsigned int res)
1671{
1672 res >>= 26;
1673 switch (res) {
1674 case AD1981_HP_EVENT:
1675 ad1981_hp_automute(codec);
1676 break;
1677 case AD1981_MIC_EVENT:
1678 ad1981_hp_automic(codec);
1679 break;
1680 }
1681}
1682
1683static struct hda_input_mux ad1981_hp_capture_source = {
1684 .num_items = 3,
1685 .items = {
1686 { "Mic", 0x0 },
1687 { "Docking-Station", 0x1 },
1688 { "Mix", 0x2 },
1689 },
1690};
1691
1692static struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001693 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001694 {
1695 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001696 .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
Takashi Iwai18a815d2006-03-01 19:54:39 +01001697 .name = "Master Playback Switch",
1698 .info = ad198x_eapd_info,
1699 .get = ad198x_eapd_get,
1700 .put = ad1981_hp_master_sw_put,
1701 .private_value = 0x05,
1702 },
1703 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1704 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1705#if 0
1706 /* FIXME: analog mic/line loopback doesn't work with my tests...
1707 * (although recording is OK)
1708 */
1709 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1710 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1711 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1712 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1713 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1714 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1715 /* FIXME: does this laptop have analog CD connection? */
1716 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1717 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1718#endif
1719 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1720 HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
1721 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1722 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1723 {
1724 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1725 .name = "Capture Source",
1726 .info = ad198x_mux_enum_info,
1727 .get = ad198x_mux_enum_get,
1728 .put = ad198x_mux_enum_put,
1729 },
1730 { } /* end */
1731};
1732
1733/* initialize jack-sensing, too */
1734static int ad1981_hp_init(struct hda_codec *codec)
1735{
1736 ad198x_init(codec);
1737 ad1981_hp_automute(codec);
1738 ad1981_hp_automic(codec);
1739 return 0;
1740}
1741
Tobin Davis18768992007-03-12 22:20:51 +01001742/* configuration for Toshiba Laptops */
1743static struct hda_verb ad1981_toshiba_init_verbs[] = {
1744 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1745 /* pin sensing on HP and Mic jacks */
1746 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1747 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1748 {}
1749};
1750
1751static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
1752 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1753 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1754 { }
1755};
1756
Takashi Iwai01686c52006-04-18 12:54:11 +02001757/* configuration for Lenovo Thinkpad T60 */
1758static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
1759 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1760 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1761 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1762 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1763 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1764 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1765 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1766 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1767 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1768 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1769 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1770 {
1771 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1772 .name = "Capture Source",
1773 .info = ad198x_mux_enum_info,
1774 .get = ad198x_mux_enum_get,
1775 .put = ad198x_mux_enum_put,
1776 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001777 /* identical with AD1983 */
1778 {
1779 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1780 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1781 .info = ad1983_spdif_route_info,
1782 .get = ad1983_spdif_route_get,
1783 .put = ad1983_spdif_route_put,
1784 },
Takashi Iwai01686c52006-04-18 12:54:11 +02001785 { } /* end */
1786};
1787
1788static struct hda_input_mux ad1981_thinkpad_capture_source = {
1789 .num_items = 3,
1790 .items = {
1791 { "Mic", 0x0 },
1792 { "Mix", 0x2 },
1793 { "CD", 0x4 },
1794 },
1795};
1796
Takashi Iwai18a815d2006-03-01 19:54:39 +01001797/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001798enum {
1799 AD1981_BASIC,
1800 AD1981_HP,
1801 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001802 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001803 AD1981_MODELS
1804};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001805
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001806static const char *ad1981_models[AD1981_MODELS] = {
1807 [AD1981_HP] = "hp",
1808 [AD1981_THINKPAD] = "thinkpad",
1809 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001810 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001811};
1812
1813static struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001814 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001815 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001816 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001817 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001818 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c52006-04-18 12:54:11 +02001819 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001820 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001821 /* HP nx6320 (reversed SSID, H/W bug) */
1822 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001823 {}
1824};
1825
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001826static int patch_ad1981(struct hda_codec *codec)
1827{
1828 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001829 int err, board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001830
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001831 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001832 if (spec == NULL)
1833 return -ENOMEM;
1834
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001835 codec->spec = spec;
1836
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001837 err = snd_hda_attach_beep_device(codec, 0x10);
1838 if (err < 0) {
1839 ad198x_free(codec);
1840 return err;
1841 }
1842 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
1843
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001844 spec->multiout.max_channels = 2;
1845 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1846 spec->multiout.dac_nids = ad1981_dac_nids;
1847 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001848 spec->num_adc_nids = 1;
1849 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001850 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001851 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001852 spec->num_mixers = 1;
1853 spec->mixers[0] = ad1981_mixers;
1854 spec->num_init_verbs = 1;
1855 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001856 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001857#ifdef CONFIG_SND_HDA_POWER_SAVE
1858 spec->loopback.amplist = ad1981_loopbacks;
1859#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001860 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001861
1862 codec->patch_ops = ad198x_patch_ops;
1863
Takashi Iwai18a815d2006-03-01 19:54:39 +01001864 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001865 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1866 ad1981_models,
1867 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001868 switch (board_config) {
1869 case AD1981_HP:
1870 spec->mixers[0] = ad1981_hp_mixers;
1871 spec->num_init_verbs = 2;
1872 spec->init_verbs[1] = ad1981_hp_init_verbs;
1873 spec->multiout.dig_out_nid = 0;
1874 spec->input_mux = &ad1981_hp_capture_source;
1875
1876 codec->patch_ops.init = ad1981_hp_init;
1877 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
Daniel T Chen01f59662009-12-13 16:22:58 -05001878 /* set the upper-limit for mixer amp to 0dB for avoiding the
1879 * possible damage by overloading
1880 */
1881 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
1882 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
1883 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
1884 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
1885 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai18a815d2006-03-01 19:54:39 +01001886 break;
Takashi Iwai01686c52006-04-18 12:54:11 +02001887 case AD1981_THINKPAD:
1888 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c52006-04-18 12:54:11 +02001889 spec->input_mux = &ad1981_thinkpad_capture_source;
Daniel T Chenb8e80cf2010-03-30 13:29:28 -04001890 /* set the upper-limit for mixer amp to 0dB for avoiding the
1891 * possible damage by overloading
1892 */
1893 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
1894 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
1895 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
1896 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
1897 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai01686c52006-04-18 12:54:11 +02001898 break;
Tobin Davis18768992007-03-12 22:20:51 +01001899 case AD1981_TOSHIBA:
1900 spec->mixers[0] = ad1981_hp_mixers;
1901 spec->mixers[1] = ad1981_toshiba_mixers;
1902 spec->num_init_verbs = 2;
1903 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1904 spec->multiout.dig_out_nid = 0;
1905 spec->input_mux = &ad1981_hp_capture_source;
1906 codec->patch_ops.init = ad1981_hp_init;
1907 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1908 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001909 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01001910
1911 codec->no_trigger_sense = 1;
1912
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001913 return 0;
1914}
1915
1916
1917/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001918 * AD1988
1919 *
1920 * Output pins and routes
1921 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001922 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001923 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1924 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1925 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1926 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1927 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1928 * port-F 0x16 (mute) <- 0x2a <- 06
1929 * port-G 0x24 (mute) <- 0x27 <- 05
1930 * port-H 0x25 (mute) <- 0x28 <- 0a
1931 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1932 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001933 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1934 * (*) 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 +01001935 *
1936 * Input pins and routes
1937 *
1938 * pin boost mix input # / adc input #
1939 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1940 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1941 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1942 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1943 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1944 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1945 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1946 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1947 *
1948 *
1949 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001950 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001951 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001952 *
1953 * Inputs of Analog Mix (0x20)
1954 * 0:Port-B (front mic)
1955 * 1:Port-C/G/H (line-in)
1956 * 2:Port-A
1957 * 3:Port-D (line-in/2)
1958 * 4:Port-E/G/H (mic-in)
1959 * 5:Port-F (mic2-in)
1960 * 6:CD
1961 * 7:Beep
1962 *
1963 * ADC selection
1964 * 0:Port-A
1965 * 1:Port-B (front mic-in)
1966 * 2:Port-C (line-in)
1967 * 3:Port-F (mic2-in)
1968 * 4:Port-E (mic-in)
1969 * 5:CD
1970 * 6:Port-G
1971 * 7:Port-H
1972 * 8:Port-D (line-in/2)
1973 * 9:Mix
1974 *
1975 * Proposed pin assignments by the datasheet
1976 *
1977 * 6-stack
1978 * Port-A front headphone
1979 * B front mic-in
1980 * C rear line-in
1981 * D rear front-out
1982 * E rear mic-in
1983 * F rear surround
1984 * G rear CLFE
1985 * H rear side
1986 *
1987 * 3-stack
1988 * Port-A front headphone
1989 * B front mic
1990 * C rear line-in/surround
1991 * D rear front-out
1992 * E rear mic-in/CLFE
1993 *
1994 * laptop
1995 * Port-A headphone
1996 * B mic-in
1997 * C docking station
1998 * D internal speaker (with EAPD)
1999 * E/F quad mic array
2000 */
2001
2002
2003/* models */
2004enum {
2005 AD1988_6STACK,
2006 AD1988_6STACK_DIG,
2007 AD1988_3STACK,
2008 AD1988_3STACK_DIG,
2009 AD1988_LAPTOP,
2010 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01002011 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002012 AD1988_MODEL_LAST,
2013};
2014
Takashi Iwaid32410b12005-11-24 16:06:23 +01002015/* reivision id to check workarounds */
2016#define AD1988A_REV2 0x100200
2017
Takashi Iwai1a806f42006-07-03 15:58:16 +02002018#define is_rev2(codec) \
2019 ((codec)->vendor_id == 0x11d41988 && \
2020 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002021
2022/*
2023 * mixers
2024 */
2025
Takashi Iwaid32410b12005-11-24 16:06:23 +01002026static hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002027 0x04, 0x06, 0x05, 0x0a
2028};
2029
Takashi Iwaid32410b12005-11-24 16:06:23 +01002030static hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002031 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002032};
2033
2034/* for AD1988A revision-2, DAC2-4 are swapped */
2035static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
2036 0x04, 0x05, 0x0a, 0x06
2037};
2038
2039static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002040 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002041};
2042
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002043static hda_nid_t ad1988_adc_nids[3] = {
2044 0x08, 0x09, 0x0f
2045};
2046
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002047static hda_nid_t ad1988_capsrc_nids[3] = {
2048 0x0c, 0x0d, 0x0e
2049};
2050
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002051#define AD1988_SPDIF_OUT 0x02
2052#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002053#define AD1988_SPDIF_IN 0x07
2054
Takashi Iwai3a08e302009-02-13 11:37:08 +01002055static hda_nid_t ad1989b_slave_dig_outs[] = {
2056 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002057};
2058
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002059static struct hda_input_mux ad1988_6stack_capture_source = {
2060 .num_items = 5,
2061 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002062 { "Front Mic", 0x1 }, /* port-B */
2063 { "Line", 0x2 }, /* port-C */
2064 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002065 { "CD", 0x5 },
2066 { "Mix", 0x9 },
2067 },
2068};
2069
2070static struct hda_input_mux ad1988_laptop_capture_source = {
2071 .num_items = 3,
2072 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002073 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002074 { "CD", 0x5 },
2075 { "Mix", 0x9 },
2076 },
2077};
2078
2079/*
2080 */
2081static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
2082 struct snd_ctl_elem_info *uinfo)
2083{
2084 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2085 struct ad198x_spec *spec = codec->spec;
2086 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
2087 spec->num_channel_mode);
2088}
2089
2090static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
2091 struct snd_ctl_elem_value *ucontrol)
2092{
2093 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2094 struct ad198x_spec *spec = codec->spec;
2095 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
2096 spec->num_channel_mode, spec->multiout.max_channels);
2097}
2098
2099static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
2100 struct snd_ctl_elem_value *ucontrol)
2101{
2102 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2103 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002104 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
2105 spec->num_channel_mode,
2106 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02002107 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02002108 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002109 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002110}
2111
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002112/* 6-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002113static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002114 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2115 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
2116 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2117 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
2118 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002119 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002120};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002121
Takashi Iwaid32410b12005-11-24 16:06:23 +01002122static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
2123 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2124 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2125 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2126 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2127 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002128 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002129};
2130
2131static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002132 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2133 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2134 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2135 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2136 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2137 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2138 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2139
2140 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2141 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2142 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2143 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2144 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2145 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2146 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2147 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2148
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002149 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002150 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2151
2152 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2153 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2154
2155 { } /* end */
2156};
2157
2158/* 3-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002159static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002160 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002161 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002162 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2163 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002164 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002165};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002166
Takashi Iwaid32410b12005-11-24 16:06:23 +01002167static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
2168 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002169 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2170 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2171 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002172 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002173};
2174
2175static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002176 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002177 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2178 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2179 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002180 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2181 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2182
2183 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2184 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2185 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2186 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2187 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2188 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2189 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2190 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2191
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002192 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002193 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2194
2195 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2196 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2197 {
2198 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2199 .name = "Channel Mode",
2200 .info = ad198x_ch_mode_info,
2201 .get = ad198x_ch_mode_get,
2202 .put = ad198x_ch_mode_put,
2203 },
2204
2205 { } /* end */
2206};
2207
2208/* laptop mode */
2209static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
2210 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2211 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2212 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2213
2214 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2215 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2216 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2217 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2218 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2219 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2220
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002221 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002222 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2223
2224 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2225
2226 {
2227 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2228 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002229 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01002230 .info = ad198x_eapd_info,
2231 .get = ad198x_eapd_get,
2232 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01002233 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002234 },
2235
2236 { } /* end */
2237};
2238
2239/* capture */
2240static struct snd_kcontrol_new ad1988_capture_mixers[] = {
2241 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2242 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2243 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2244 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2245 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2246 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2247 {
2248 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2249 /* The multiple "Capture Source" controls confuse alsamixer
2250 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002251 */
2252 /* .name = "Capture Source", */
2253 .name = "Input Source",
2254 .count = 3,
2255 .info = ad198x_mux_enum_info,
2256 .get = ad198x_mux_enum_get,
2257 .put = ad198x_mux_enum_put,
2258 },
2259 { } /* end */
2260};
2261
2262static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2263 struct snd_ctl_elem_info *uinfo)
2264{
2265 static char *texts[] = {
2266 "PCM", "ADC1", "ADC2", "ADC3"
2267 };
2268 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2269 uinfo->count = 1;
2270 uinfo->value.enumerated.items = 4;
2271 if (uinfo->value.enumerated.item >= 4)
2272 uinfo->value.enumerated.item = 3;
2273 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2274 return 0;
2275}
2276
2277static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2278 struct snd_ctl_elem_value *ucontrol)
2279{
2280 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2281 unsigned int sel;
2282
Takashi Iwaibddcf542007-07-24 18:04:05 +02002283 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2284 AC_AMP_GET_INPUT);
2285 if (!(sel & 0x80))
2286 ucontrol->value.enumerated.item[0] = 0;
2287 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002288 sel = snd_hda_codec_read(codec, 0x0b, 0,
2289 AC_VERB_GET_CONNECT_SEL, 0);
2290 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002291 sel++;
2292 else
2293 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002294 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002295 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002296 return 0;
2297}
2298
2299static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2300 struct snd_ctl_elem_value *ucontrol)
2301{
2302 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002303 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002304 int change;
2305
Takashi Iwai35b26722007-05-05 12:17:17 +02002306 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002307 if (val > 3)
2308 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002309 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002310 sel = snd_hda_codec_read(codec, 0x1d, 0,
2311 AC_VERB_GET_AMP_GAIN_MUTE,
2312 AC_AMP_GET_INPUT);
2313 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002314 if (change) {
2315 snd_hda_codec_write_cache(codec, 0x1d, 0,
2316 AC_VERB_SET_AMP_GAIN_MUTE,
2317 AMP_IN_UNMUTE(0));
2318 snd_hda_codec_write_cache(codec, 0x1d, 0,
2319 AC_VERB_SET_AMP_GAIN_MUTE,
2320 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002321 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002322 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002323 sel = snd_hda_codec_read(codec, 0x1d, 0,
2324 AC_VERB_GET_AMP_GAIN_MUTE,
2325 AC_AMP_GET_INPUT | 0x01);
2326 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002327 if (change) {
2328 snd_hda_codec_write_cache(codec, 0x1d, 0,
2329 AC_VERB_SET_AMP_GAIN_MUTE,
2330 AMP_IN_MUTE(0));
2331 snd_hda_codec_write_cache(codec, 0x1d, 0,
2332 AC_VERB_SET_AMP_GAIN_MUTE,
2333 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002334 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002335 sel = snd_hda_codec_read(codec, 0x0b, 0,
2336 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2337 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002338 if (change)
2339 snd_hda_codec_write_cache(codec, 0x0b, 0,
2340 AC_VERB_SET_CONNECT_SEL,
2341 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002342 }
2343 return change;
2344}
2345
2346static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
2347 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2348 {
2349 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2350 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002351 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002352 .info = ad1988_spdif_playback_source_info,
2353 .get = ad1988_spdif_playback_source_get,
2354 .put = ad1988_spdif_playback_source_put,
2355 },
2356 { } /* end */
2357};
2358
2359static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
2360 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2361 { } /* end */
2362};
2363
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002364static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
2365 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002366 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002367 { } /* end */
2368};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002369
2370/*
2371 * initialization verbs
2372 */
2373
2374/*
2375 * for 6-stack (+dig)
2376 */
2377static struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002378 /* Front, Surround, CLFE, side DAC; unmute as default */
2379 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2380 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2381 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2382 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002383 /* Port-A front headphon path */
2384 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2385 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2386 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2387 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2388 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2389 /* Port-D line-out path */
2390 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2391 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2392 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2393 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2394 /* Port-F surround path */
2395 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2396 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2397 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2398 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2399 /* Port-G CLFE path */
2400 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2401 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2402 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2403 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2404 /* Port-H side path */
2405 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2406 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2407 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2408 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2409 /* Mono out path */
2410 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2411 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2412 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2413 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2414 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2415 /* Port-B front mic-in path */
2416 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2417 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2418 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2419 /* Port-C line-in path */
2420 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2421 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2422 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2423 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2424 /* Port-E mic-in path */
2425 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2426 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2427 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2428 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002429 /* Analog CD Input */
2430 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002431 /* Analog Mix output amp */
2432 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002433
2434 { }
2435};
2436
2437static struct hda_verb ad1988_capture_init_verbs[] = {
2438 /* mute analog mix */
2439 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2440 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2441 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2442 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2443 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2444 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2445 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2446 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2447 /* select ADCs - front-mic */
2448 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2449 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2450 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002451
2452 { }
2453};
2454
2455static struct hda_verb ad1988_spdif_init_verbs[] = {
2456 /* SPDIF out sel */
2457 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2458 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2459 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002460 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002461 /* SPDIF out pin */
2462 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002463
2464 { }
2465};
2466
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002467static struct hda_verb ad1988_spdif_in_init_verbs[] = {
2468 /* unmute SPDIF input pin */
2469 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2470 { }
2471};
2472
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002473/* AD1989 has no ADC -> SPDIF route */
2474static struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002475 /* SPDIF-1 out pin */
2476 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002477 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002478 /* SPDIF-2/HDMI out pin */
2479 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2480 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002481 { }
2482};
2483
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002484/*
2485 * verbs for 3stack (+dig)
2486 */
2487static struct hda_verb ad1988_3stack_ch2_init[] = {
2488 /* set port-C to line-in */
2489 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2490 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2491 /* set port-E to mic-in */
2492 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2493 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2494 { } /* end */
2495};
2496
2497static struct hda_verb ad1988_3stack_ch6_init[] = {
2498 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002499 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002500 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002501 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002502 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002503 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002504 { } /* end */
2505};
2506
2507static struct hda_channel_mode ad1988_3stack_modes[2] = {
2508 { 2, ad1988_3stack_ch2_init },
2509 { 6, ad1988_3stack_ch6_init },
2510};
2511
2512static struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002513 /* Front, Surround, CLFE, side DAC; unmute as default */
2514 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2515 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2516 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2517 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002518 /* Port-A front headphon path */
2519 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2520 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2521 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2522 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2523 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2524 /* Port-D line-out path */
2525 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2526 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2527 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2528 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2529 /* Mono out path */
2530 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2531 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2532 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2533 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2534 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2535 /* Port-B front mic-in path */
2536 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2537 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2538 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002539 /* Port-C line-in/surround path - 6ch mode as default */
2540 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2541 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002542 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002543 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002544 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002545 /* Port-E mic-in/CLFE path - 6ch mode as default */
2546 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2547 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002548 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002549 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002550 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2551 /* mute analog mix */
2552 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2553 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2554 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2555 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2556 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2557 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2558 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2559 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2560 /* select ADCs - front-mic */
2561 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2562 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2563 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002564 /* Analog Mix output amp */
2565 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002566 { }
2567};
2568
2569/*
2570 * verbs for laptop mode (+dig)
2571 */
2572static struct hda_verb ad1988_laptop_hp_on[] = {
2573 /* unmute port-A and mute port-D */
2574 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2575 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2576 { } /* end */
2577};
2578static struct hda_verb ad1988_laptop_hp_off[] = {
2579 /* mute port-A and unmute port-D */
2580 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2581 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2582 { } /* end */
2583};
2584
2585#define AD1988_HP_EVENT 0x01
2586
2587static struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002588 /* Front, Surround, CLFE, side DAC; unmute as default */
2589 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2590 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2591 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2592 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002593 /* Port-A front headphon path */
2594 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2595 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2596 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2597 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2598 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2599 /* unsolicited event for pin-sense */
2600 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2601 /* Port-D line-out path + EAPD */
2602 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2603 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2604 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2605 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2606 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2607 /* Mono out path */
2608 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2609 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2610 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2611 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2612 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2613 /* Port-B mic-in path */
2614 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2615 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2616 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2617 /* Port-C docking station - try to output */
2618 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2619 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2620 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2621 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2622 /* mute analog mix */
2623 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2624 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2625 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2626 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2627 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2628 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2629 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2630 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2631 /* select ADCs - mic */
2632 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2633 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2634 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002635 /* Analog Mix output amp */
2636 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002637 { }
2638};
2639
2640static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2641{
2642 if ((res >> 26) != AD1988_HP_EVENT)
2643 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01002644 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002645 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2646 else
2647 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2648}
2649
Takashi Iwaicb53c622007-08-10 17:21:45 +02002650#ifdef CONFIG_SND_HDA_POWER_SAVE
2651static struct hda_amp_list ad1988_loopbacks[] = {
2652 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2653 { 0x20, HDA_INPUT, 1 }, /* Line */
2654 { 0x20, HDA_INPUT, 4 }, /* Mic */
2655 { 0x20, HDA_INPUT, 6 }, /* CD */
2656 { } /* end */
2657};
2658#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002659
2660/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002661 * Automatic parse of I/O pins from the BIOS configuration
2662 */
2663
Takashi Iwaid32410b12005-11-24 16:06:23 +01002664enum {
2665 AD_CTL_WIDGET_VOL,
2666 AD_CTL_WIDGET_MUTE,
2667 AD_CTL_BIND_MUTE,
2668};
2669static struct snd_kcontrol_new ad1988_control_templates[] = {
2670 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2671 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2672 HDA_BIND_MUTE(NULL, 0, 0, 0),
2673};
2674
2675/* add dynamic controls */
2676static int add_control(struct ad198x_spec *spec, int type, const char *name,
2677 unsigned long val)
2678{
2679 struct snd_kcontrol_new *knew;
2680
Takashi Iwai603c4012008-07-30 15:01:44 +02002681 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2682 knew = snd_array_new(&spec->kctls);
2683 if (!knew)
2684 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002685 *knew = ad1988_control_templates[type];
2686 knew->name = kstrdup(name, GFP_KERNEL);
2687 if (! knew->name)
2688 return -ENOMEM;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +01002689 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01002690 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002691 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002692 return 0;
2693}
2694
2695#define AD1988_PIN_CD_NID 0x18
2696#define AD1988_PIN_BEEP_NID 0x10
2697
2698static hda_nid_t ad1988_mixer_nids[8] = {
2699 /* A B C D E F G H */
2700 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2701};
2702
2703static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2704{
2705 static hda_nid_t idx_to_dac[8] = {
2706 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002707 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002708 };
2709 static hda_nid_t idx_to_dac_rev2[8] = {
2710 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002711 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002712 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002713 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002714 return idx_to_dac_rev2[idx];
2715 else
2716 return idx_to_dac[idx];
2717}
2718
2719static hda_nid_t ad1988_boost_nids[8] = {
2720 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2721};
2722
2723static int ad1988_pin_idx(hda_nid_t nid)
2724{
2725 static hda_nid_t ad1988_io_pins[8] = {
2726 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2727 };
2728 int i;
2729 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2730 if (ad1988_io_pins[i] == nid)
2731 return i;
2732 return 0; /* should be -1 */
2733}
2734
2735static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2736{
2737 static int loopback_idx[8] = {
2738 2, 0, 1, 3, 4, 5, 1, 4
2739 };
2740 switch (nid) {
2741 case AD1988_PIN_CD_NID:
2742 return 6;
2743 default:
2744 return loopback_idx[ad1988_pin_idx(nid)];
2745 }
2746}
2747
2748static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2749{
2750 static int adc_idx[8] = {
2751 0, 1, 2, 8, 4, 3, 6, 7
2752 };
2753 switch (nid) {
2754 case AD1988_PIN_CD_NID:
2755 return 5;
2756 default:
2757 return adc_idx[ad1988_pin_idx(nid)];
2758 }
2759}
2760
2761/* fill in the dac_nids table from the parsed pin configuration */
2762static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2763 const struct auto_pin_cfg *cfg)
2764{
2765 struct ad198x_spec *spec = codec->spec;
2766 int i, idx;
2767
2768 spec->multiout.dac_nids = spec->private_dac_nids;
2769
2770 /* check the pins hardwired to audio widget */
2771 for (i = 0; i < cfg->line_outs; i++) {
2772 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2773 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2774 }
2775 spec->multiout.num_dacs = cfg->line_outs;
2776 return 0;
2777}
2778
2779/* add playback controls from the parsed DAC table */
2780static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2781 const struct auto_pin_cfg *cfg)
2782{
2783 char name[32];
2784 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2785 hda_nid_t nid;
2786 int i, err;
2787
2788 for (i = 0; i < cfg->line_outs; i++) {
2789 hda_nid_t dac = spec->multiout.dac_nids[i];
2790 if (! dac)
2791 continue;
2792 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2793 if (i == 2) {
2794 /* Center/LFE */
2795 err = add_control(spec, AD_CTL_WIDGET_VOL,
2796 "Center Playback Volume",
2797 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2798 if (err < 0)
2799 return err;
2800 err = add_control(spec, AD_CTL_WIDGET_VOL,
2801 "LFE Playback Volume",
2802 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2803 if (err < 0)
2804 return err;
2805 err = add_control(spec, AD_CTL_BIND_MUTE,
2806 "Center Playback Switch",
2807 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2808 if (err < 0)
2809 return err;
2810 err = add_control(spec, AD_CTL_BIND_MUTE,
2811 "LFE Playback Switch",
2812 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2813 if (err < 0)
2814 return err;
2815 } else {
2816 sprintf(name, "%s Playback Volume", chname[i]);
2817 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2818 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2819 if (err < 0)
2820 return err;
2821 sprintf(name, "%s Playback Switch", chname[i]);
2822 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2823 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2824 if (err < 0)
2825 return err;
2826 }
2827 }
2828 return 0;
2829}
2830
2831/* add playback controls for speaker and HP outputs */
2832static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2833 const char *pfx)
2834{
2835 struct ad198x_spec *spec = codec->spec;
2836 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02002837 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002838 char name[32];
2839
2840 if (! pin)
2841 return 0;
2842
2843 idx = ad1988_pin_idx(pin);
2844 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02002845 /* check whether the corresponding DAC was already taken */
2846 for (i = 0; i < spec->autocfg.line_outs; i++) {
2847 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2848 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
2849 if (dac == nid)
2850 break;
2851 }
2852 if (i >= spec->autocfg.line_outs) {
2853 /* specify the DAC as the extra output */
2854 if (!spec->multiout.hp_nid)
2855 spec->multiout.hp_nid = nid;
2856 else
2857 spec->multiout.extra_out_nid[0] = nid;
2858 /* control HP volume/switch on the output mixer amp */
2859 sprintf(name, "%s Playback Volume", pfx);
2860 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2861 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2862 if (err < 0)
2863 return err;
2864 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01002865 nid = ad1988_mixer_nids[idx];
2866 sprintf(name, "%s Playback Switch", pfx);
2867 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2868 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2869 return err;
2870 return 0;
2871}
2872
2873/* create input playback/capture controls for the given pin */
2874static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2875 const char *ctlname, int boost)
2876{
2877 char name[32];
2878 int err, idx;
2879
2880 sprintf(name, "%s Playback Volume", ctlname);
2881 idx = ad1988_pin_to_loopback_idx(pin);
2882 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2883 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2884 return err;
2885 sprintf(name, "%s Playback Switch", ctlname);
2886 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2887 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2888 return err;
2889 if (boost) {
2890 hda_nid_t bnid;
2891 idx = ad1988_pin_idx(pin);
2892 bnid = ad1988_boost_nids[idx];
2893 if (bnid) {
2894 sprintf(name, "%s Boost", ctlname);
2895 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2896 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2897
2898 }
2899 }
2900 return 0;
2901}
2902
2903/* create playback/capture controls for input pins */
2904static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2905 const struct auto_pin_cfg *cfg)
2906{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002907 struct hda_input_mux *imux = &spec->private_imux;
2908 int i, err;
2909
2910 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002911 err = new_analog_input(spec, cfg->input_pins[i],
2912 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002913 i <= AUTO_PIN_FRONT_MIC);
2914 if (err < 0)
2915 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002916 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002917 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2918 imux->num_items++;
2919 }
2920 imux->items[imux->num_items].label = "Mix";
2921 imux->items[imux->num_items].index = 9;
2922 imux->num_items++;
2923
2924 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2925 "Analog Mix Playback Volume",
2926 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2927 return err;
2928 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2929 "Analog Mix Playback Switch",
2930 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2931 return err;
2932
2933 return 0;
2934}
2935
2936static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2937 hda_nid_t nid, int pin_type,
2938 int dac_idx)
2939{
2940 /* set as output */
2941 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2942 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2943 switch (nid) {
2944 case 0x11: /* port-A - DAC 04 */
2945 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2946 break;
2947 case 0x14: /* port-B - DAC 06 */
2948 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2949 break;
2950 case 0x15: /* port-C - DAC 05 */
2951 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2952 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002953 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002954 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2955 break;
2956 case 0x13: /* mono - DAC 04 */
2957 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2958 break;
2959 }
2960}
2961
2962static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2963{
2964 struct ad198x_spec *spec = codec->spec;
2965 int i;
2966
2967 for (i = 0; i < spec->autocfg.line_outs; i++) {
2968 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2969 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2970 }
2971}
2972
2973static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2974{
2975 struct ad198x_spec *spec = codec->spec;
2976 hda_nid_t pin;
2977
Takashi Iwai82bc9552006-03-21 11:24:42 +01002978 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002979 if (pin) /* connect to front */
2980 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002981 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002982 if (pin) /* connect to front */
2983 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2984}
2985
2986static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2987{
2988 struct ad198x_spec *spec = codec->spec;
2989 int i, idx;
2990
2991 for (i = 0; i < AUTO_PIN_LAST; i++) {
2992 hda_nid_t nid = spec->autocfg.input_pins[i];
2993 if (! nid)
2994 continue;
2995 switch (nid) {
2996 case 0x15: /* port-C */
2997 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2998 break;
2999 case 0x17: /* port-E */
3000 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
3001 break;
3002 }
3003 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
3004 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
3005 if (nid != AD1988_PIN_CD_NID)
3006 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
3007 AMP_OUT_MUTE);
3008 idx = ad1988_pin_idx(nid);
3009 if (ad1988_boost_nids[idx])
3010 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
3011 AC_VERB_SET_AMP_GAIN_MUTE,
3012 AMP_OUT_ZERO);
3013 }
3014}
3015
3016/* parse the BIOS configuration and set up the alc_spec */
3017/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
3018static int ad1988_parse_auto_config(struct hda_codec *codec)
3019{
3020 struct ad198x_spec *spec = codec->spec;
3021 int err;
3022
Kailang Yangdf694da2005-12-05 19:42:22 +01003023 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003024 return err;
3025 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
3026 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01003027 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003028 return 0; /* can't find valid BIOS pin config */
3029 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01003030 (err = ad1988_auto_create_extra_out(codec,
3031 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003032 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003033 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003034 "Headphone")) < 0 ||
3035 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
3036 return err;
3037
3038 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3039
Takashi Iwai0852d7a2009-02-11 11:35:15 +01003040 if (spec->autocfg.dig_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003041 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3042 if (spec->autocfg.dig_in_pin)
3043 spec->dig_in_nid = AD1988_SPDIF_IN;
3044
Takashi Iwai603c4012008-07-30 15:01:44 +02003045 if (spec->kctls.list)
3046 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003047
3048 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
3049
3050 spec->input_mux = &spec->private_imux;
3051
3052 return 1;
3053}
3054
3055/* init callback for auto-configuration model -- overriding the default init */
3056static int ad1988_auto_init(struct hda_codec *codec)
3057{
3058 ad198x_init(codec);
3059 ad1988_auto_init_multi_out(codec);
3060 ad1988_auto_init_extra_out(codec);
3061 ad1988_auto_init_analog_input(codec);
3062 return 0;
3063}
3064
3065
3066/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003067 */
3068
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003069static const char *ad1988_models[AD1988_MODEL_LAST] = {
3070 [AD1988_6STACK] = "6stack",
3071 [AD1988_6STACK_DIG] = "6stack-dig",
3072 [AD1988_3STACK] = "3stack",
3073 [AD1988_3STACK_DIG] = "3stack-dig",
3074 [AD1988_LAPTOP] = "laptop",
3075 [AD1988_LAPTOP_DIG] = "laptop-dig",
3076 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003077};
3078
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003079static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01003080 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01003081 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02003082 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07003083 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003084 {}
3085};
3086
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003087static int patch_ad1988(struct hda_codec *codec)
3088{
3089 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003090 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003091
3092 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3093 if (spec == NULL)
3094 return -ENOMEM;
3095
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003096 codec->spec = spec;
3097
Takashi Iwai1a806f42006-07-03 15:58:16 +02003098 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003099 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
3100
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003101 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003102 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003103 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02003104 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3105 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003106 board_config = AD1988_AUTO;
3107 }
3108
3109 if (board_config == AD1988_AUTO) {
3110 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003111 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003112 if (err < 0) {
3113 ad198x_free(codec);
3114 return err;
3115 } else if (! err) {
3116 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
3117 board_config = AD1988_6STACK;
3118 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003119 }
3120
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003121 err = snd_hda_attach_beep_device(codec, 0x10);
3122 if (err < 0) {
3123 ad198x_free(codec);
3124 return err;
3125 }
3126 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3127
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003128 switch (board_config) {
3129 case AD1988_6STACK:
3130 case AD1988_6STACK_DIG:
3131 spec->multiout.max_channels = 8;
3132 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003133 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003134 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3135 else
3136 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003137 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003138 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003139 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003140 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3141 else
3142 spec->mixers[0] = ad1988_6stack_mixers1;
3143 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003144 spec->num_init_verbs = 1;
3145 spec->init_verbs[0] = ad1988_6stack_init_verbs;
3146 if (board_config == AD1988_6STACK_DIG) {
3147 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3148 spec->dig_in_nid = AD1988_SPDIF_IN;
3149 }
3150 break;
3151 case AD1988_3STACK:
3152 case AD1988_3STACK_DIG:
3153 spec->multiout.max_channels = 6;
3154 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003155 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003156 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3157 else
3158 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003159 spec->input_mux = &ad1988_6stack_capture_source;
3160 spec->channel_mode = ad1988_3stack_modes;
3161 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003162 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003163 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003164 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3165 else
3166 spec->mixers[0] = ad1988_3stack_mixers1;
3167 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003168 spec->num_init_verbs = 1;
3169 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3170 if (board_config == AD1988_3STACK_DIG)
3171 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3172 break;
3173 case AD1988_LAPTOP:
3174 case AD1988_LAPTOP_DIG:
3175 spec->multiout.max_channels = 2;
3176 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003177 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003178 spec->input_mux = &ad1988_laptop_capture_source;
3179 spec->num_mixers = 1;
3180 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01003181 spec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003182 spec->num_init_verbs = 1;
3183 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3184 if (board_config == AD1988_LAPTOP_DIG)
3185 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3186 break;
3187 }
3188
Takashi Iwaid32410b12005-11-24 16:06:23 +01003189 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3190 spec->adc_nids = ad1988_adc_nids;
3191 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003192 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3193 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3194 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003195 if (codec->vendor_id >= 0x11d4989a) {
3196 spec->mixers[spec->num_mixers++] =
3197 ad1989_spdif_out_mixers;
3198 spec->init_verbs[spec->num_init_verbs++] =
3199 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003200 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003201 } else {
3202 spec->mixers[spec->num_mixers++] =
3203 ad1988_spdif_out_mixers;
3204 spec->init_verbs[spec->num_init_verbs++] =
3205 ad1988_spdif_init_verbs;
3206 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003207 }
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003208 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003209 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003210 spec->init_verbs[spec->num_init_verbs++] =
3211 ad1988_spdif_in_init_verbs;
3212 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003213
3214 codec->patch_ops = ad198x_patch_ops;
3215 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003216 case AD1988_AUTO:
3217 codec->patch_ops.init = ad1988_auto_init;
3218 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003219 case AD1988_LAPTOP:
3220 case AD1988_LAPTOP_DIG:
3221 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3222 break;
3223 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003224#ifdef CONFIG_SND_HDA_POWER_SAVE
3225 spec->loopback.amplist = ad1988_loopbacks;
3226#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003227 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003228
Takashi Iwai729d55b2009-12-25 22:49:01 +01003229 codec->no_trigger_sense = 1;
3230
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003231 return 0;
3232}
3233
3234
3235/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003236 * AD1884 / AD1984
3237 *
3238 * port-B - front line/mic-in
3239 * port-E - aux in/out
3240 * port-F - aux in/out
3241 * port-C - rear line/mic-in
3242 * port-D - rear line/hp-out
3243 * port-A - front line/hp-out
3244 *
3245 * AD1984 = AD1884 + two digital mic-ins
3246 *
3247 * FIXME:
3248 * For simplicity, we share the single DAC for both HP and line-outs
3249 * right now. The inidividual playbacks could be easily implemented,
3250 * but no build-up framework is given, so far.
3251 */
3252
3253static hda_nid_t ad1884_dac_nids[1] = {
3254 0x04,
3255};
3256
3257static hda_nid_t ad1884_adc_nids[2] = {
3258 0x08, 0x09,
3259};
3260
3261static hda_nid_t ad1884_capsrc_nids[2] = {
3262 0x0c, 0x0d,
3263};
3264
3265#define AD1884_SPDIF_OUT 0x02
3266
3267static struct hda_input_mux ad1884_capture_source = {
3268 .num_items = 4,
3269 .items = {
3270 { "Front Mic", 0x0 },
3271 { "Mic", 0x1 },
3272 { "CD", 0x2 },
3273 { "Mix", 0x3 },
3274 },
3275};
3276
3277static struct snd_kcontrol_new ad1884_base_mixers[] = {
3278 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3279 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3280 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3281 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3282 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3283 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3284 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3285 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3286 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3287 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3288 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3289 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003290 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
3291 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3292 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3293 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3294 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3295 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3296 {
3297 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3298 /* The multiple "Capture Source" controls confuse alsamixer
3299 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003300 */
3301 /* .name = "Capture Source", */
3302 .name = "Input Source",
3303 .count = 2,
3304 .info = ad198x_mux_enum_info,
3305 .get = ad198x_mux_enum_get,
3306 .put = ad198x_mux_enum_put,
3307 },
3308 /* SPDIF controls */
3309 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3310 {
3311 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3312 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3313 /* identical with ad1983 */
3314 .info = ad1983_spdif_route_info,
3315 .get = ad1983_spdif_route_get,
3316 .put = ad1983_spdif_route_put,
3317 },
3318 { } /* end */
3319};
3320
3321static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3322 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3323 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3324 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003325 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003326 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003327 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003328 { } /* end */
3329};
3330
3331/*
3332 * initialization verbs
3333 */
3334static struct hda_verb ad1884_init_verbs[] = {
3335 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003336 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3337 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003338 /* Port-A (HP) mixer */
3339 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3340 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3341 /* Port-A pin */
3342 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3343 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3344 /* HP selector - select DAC2 */
3345 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3346 /* Port-D (Line-out) mixer */
3347 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3348 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3349 /* Port-D pin */
3350 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3351 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3352 /* Mono-out mixer */
3353 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3354 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3355 /* Mono-out pin */
3356 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3357 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3358 /* Mono selector */
3359 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3360 /* Port-B (front mic) pin */
3361 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003362 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003363 /* Port-C (rear mic) pin */
3364 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003365 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003366 /* Analog mixer; mute as default */
3367 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3368 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3369 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3370 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3371 /* Analog Mix output amp */
3372 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3373 /* SPDIF output selector */
3374 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3375 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3376 { } /* end */
3377};
3378
Takashi Iwaicb53c622007-08-10 17:21:45 +02003379#ifdef CONFIG_SND_HDA_POWER_SAVE
3380static struct hda_amp_list ad1884_loopbacks[] = {
3381 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3382 { 0x20, HDA_INPUT, 1 }, /* Mic */
3383 { 0x20, HDA_INPUT, 2 }, /* CD */
3384 { 0x20, HDA_INPUT, 4 }, /* Docking */
3385 { } /* end */
3386};
3387#endif
3388
Takashi Iwai2134ea42008-01-10 16:53:55 +01003389static const char *ad1884_slave_vols[] = {
3390 "PCM Playback Volume",
3391 "Mic Playback Volume",
3392 "Mono Playback Volume",
3393 "Front Mic Playback Volume",
3394 "Mic Playback Volume",
3395 "CD Playback Volume",
3396 "Internal Mic Playback Volume",
Akinobu Mitabca68462009-04-06 18:42:42 +09003397 "Docking Mic Playback Volume",
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003398 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003399 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003400 NULL
3401};
3402
Takashi Iwai2bac6472007-05-18 18:21:41 +02003403static int patch_ad1884(struct hda_codec *codec)
3404{
3405 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003406 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003407
3408 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3409 if (spec == NULL)
3410 return -ENOMEM;
3411
Takashi Iwai2bac6472007-05-18 18:21:41 +02003412 codec->spec = spec;
3413
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003414 err = snd_hda_attach_beep_device(codec, 0x10);
3415 if (err < 0) {
3416 ad198x_free(codec);
3417 return err;
3418 }
3419 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3420
Takashi Iwai2bac6472007-05-18 18:21:41 +02003421 spec->multiout.max_channels = 2;
3422 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3423 spec->multiout.dac_nids = ad1884_dac_nids;
3424 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3425 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3426 spec->adc_nids = ad1884_adc_nids;
3427 spec->capsrc_nids = ad1884_capsrc_nids;
3428 spec->input_mux = &ad1884_capture_source;
3429 spec->num_mixers = 1;
3430 spec->mixers[0] = ad1884_base_mixers;
3431 spec->num_init_verbs = 1;
3432 spec->init_verbs[0] = ad1884_init_verbs;
3433 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003434#ifdef CONFIG_SND_HDA_POWER_SAVE
3435 spec->loopback.amplist = ad1884_loopbacks;
3436#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003437 spec->vmaster_nid = 0x04;
3438 /* we need to cover all playback volumes */
3439 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003440
3441 codec->patch_ops = ad198x_patch_ops;
3442
Takashi Iwai729d55b2009-12-25 22:49:01 +01003443 codec->no_trigger_sense = 1;
3444
Takashi Iwai2bac6472007-05-18 18:21:41 +02003445 return 0;
3446}
3447
3448/*
3449 * Lenovo Thinkpad T61/X61
3450 */
3451static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003452 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003453 .items = {
3454 { "Mic", 0x0 },
3455 { "Internal Mic", 0x1 },
3456 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003457 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003458 },
3459};
3460
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003461
3462/*
3463 * Dell Precision T3400
3464 */
3465static struct hda_input_mux ad1984_dell_desktop_capture_source = {
3466 .num_items = 3,
3467 .items = {
3468 { "Front Mic", 0x0 },
3469 { "Line-In", 0x1 },
3470 { "Mix", 0x3 },
3471 },
3472};
3473
3474
Takashi Iwai2bac6472007-05-18 18:21:41 +02003475static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3476 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3477 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3478 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3479 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3480 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3481 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3482 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3483 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3484 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3485 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003486 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3487 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003488 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003489 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3490 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3491 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3492 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3493 {
3494 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3495 /* The multiple "Capture Source" controls confuse alsamixer
3496 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003497 */
3498 /* .name = "Capture Source", */
3499 .name = "Input Source",
3500 .count = 2,
3501 .info = ad198x_mux_enum_info,
3502 .get = ad198x_mux_enum_get,
3503 .put = ad198x_mux_enum_put,
3504 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003505 /* SPDIF controls */
3506 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3507 {
3508 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3509 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3510 /* identical with ad1983 */
3511 .info = ad1983_spdif_route_info,
3512 .get = ad1983_spdif_route_get,
3513 .put = ad1983_spdif_route_put,
3514 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003515 { } /* end */
3516};
3517
3518/* additional verbs */
3519static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3520 /* Port-E (docking station mic) pin */
3521 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3522 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3523 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003524 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003525 /* Analog mixer - docking mic; mute as default */
3526 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003527 /* enable EAPD bit */
3528 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003529 { } /* end */
3530};
3531
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003532/*
3533 * Dell Precision T3400
3534 */
3535static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
3536 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3537 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3538 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3539 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3540 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3541 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3542 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3543 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3544 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003545 HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
3546 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3547 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3548 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3549 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3550 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3551 {
3552 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3553 /* The multiple "Capture Source" controls confuse alsamixer
3554 * So call somewhat different..
3555 */
3556 /* .name = "Capture Source", */
3557 .name = "Input Source",
3558 .count = 2,
3559 .info = ad198x_mux_enum_info,
3560 .get = ad198x_mux_enum_get,
3561 .put = ad198x_mux_enum_put,
3562 },
3563 { } /* end */
3564};
3565
Takashi Iwai2bac6472007-05-18 18:21:41 +02003566/* Digial MIC ADC NID 0x05 + 0x06 */
3567static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3568 struct hda_codec *codec,
3569 unsigned int stream_tag,
3570 unsigned int format,
3571 struct snd_pcm_substream *substream)
3572{
3573 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3574 stream_tag, 0, format);
3575 return 0;
3576}
3577
3578static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3579 struct hda_codec *codec,
3580 struct snd_pcm_substream *substream)
3581{
Takashi Iwai888afa12008-03-18 09:57:50 +01003582 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003583 return 0;
3584}
3585
3586static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3587 .substreams = 2,
3588 .channels_min = 2,
3589 .channels_max = 2,
3590 .nid = 0x05,
3591 .ops = {
3592 .prepare = ad1984_pcm_dmic_prepare,
3593 .cleanup = ad1984_pcm_dmic_cleanup
3594 },
3595};
3596
3597static int ad1984_build_pcms(struct hda_codec *codec)
3598{
3599 struct ad198x_spec *spec = codec->spec;
3600 struct hda_pcm *info;
3601 int err;
3602
3603 err = ad198x_build_pcms(codec);
3604 if (err < 0)
3605 return err;
3606
3607 info = spec->pcm_rec + codec->num_pcms;
3608 codec->num_pcms++;
3609 info->name = "AD1984 Digital Mic";
3610 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3611 return 0;
3612}
3613
3614/* models */
3615enum {
3616 AD1984_BASIC,
3617 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003618 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003619 AD1984_MODELS
3620};
3621
3622static const char *ad1984_models[AD1984_MODELS] = {
3623 [AD1984_BASIC] = "basic",
3624 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003625 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003626};
3627
3628static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3629 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003630 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003631 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003632 {}
3633};
3634
3635static int patch_ad1984(struct hda_codec *codec)
3636{
3637 struct ad198x_spec *spec;
3638 int board_config, err;
3639
3640 err = patch_ad1884(codec);
3641 if (err < 0)
3642 return err;
3643 spec = codec->spec;
3644 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3645 ad1984_models, ad1984_cfg_tbl);
3646 switch (board_config) {
3647 case AD1984_BASIC:
3648 /* additional digital mics */
3649 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3650 codec->patch_ops.build_pcms = ad1984_build_pcms;
3651 break;
3652 case AD1984_THINKPAD:
Jerone Youngebf00c52008-01-07 12:22:18 +01003653 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003654 spec->input_mux = &ad1984_thinkpad_capture_source;
3655 spec->mixers[0] = ad1984_thinkpad_mixers;
3656 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
3657 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003658 case AD1984_DELL_DESKTOP:
3659 spec->multiout.dig_out_nid = 0;
3660 spec->input_mux = &ad1984_dell_desktop_capture_source;
3661 spec->mixers[0] = ad1984_dell_desktop_mixers;
3662 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003663 }
3664 return 0;
3665}
3666
3667
3668/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003669 * AD1883 / AD1884A / AD1984A / AD1984B
3670 *
3671 * port-B (0x14) - front mic-in
3672 * port-E (0x1c) - rear mic-in
3673 * port-F (0x16) - CD / ext out
3674 * port-C (0x15) - rear line-in
3675 * port-D (0x12) - rear line-out
3676 * port-A (0x11) - front hp-out
3677 *
3678 * AD1984A = AD1884A + digital-mic
3679 * AD1883 = equivalent with AD1984A
3680 * AD1984B = AD1984A + extra SPDIF-out
3681 *
3682 * FIXME:
3683 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3684 */
3685
3686static hda_nid_t ad1884a_dac_nids[1] = {
3687 0x03,
3688};
3689
3690#define ad1884a_adc_nids ad1884_adc_nids
3691#define ad1884a_capsrc_nids ad1884_capsrc_nids
3692
3693#define AD1884A_SPDIF_OUT 0x02
3694
3695static struct hda_input_mux ad1884a_capture_source = {
3696 .num_items = 5,
3697 .items = {
3698 { "Front Mic", 0x0 },
3699 { "Mic", 0x4 },
3700 { "Line", 0x1 },
3701 { "CD", 0x2 },
3702 { "Mix", 0x3 },
3703 },
3704};
3705
3706static struct snd_kcontrol_new ad1884a_base_mixers[] = {
3707 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3708 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3709 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3710 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3711 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3712 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3713 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3714 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3715 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3716 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3717 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3718 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3719 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3720 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3721 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3722 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003723 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3724 HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
3725 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3726 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3727 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3728 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3729 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3730 {
3731 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3732 /* The multiple "Capture Source" controls confuse alsamixer
3733 * So call somewhat different..
3734 */
3735 /* .name = "Capture Source", */
3736 .name = "Input Source",
3737 .count = 2,
3738 .info = ad198x_mux_enum_info,
3739 .get = ad198x_mux_enum_get,
3740 .put = ad198x_mux_enum_put,
3741 },
3742 /* SPDIF controls */
3743 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3744 {
3745 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3746 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3747 /* identical with ad1983 */
3748 .info = ad1983_spdif_route_info,
3749 .get = ad1983_spdif_route_get,
3750 .put = ad1983_spdif_route_put,
3751 },
3752 { } /* end */
3753};
3754
3755/*
3756 * initialization verbs
3757 */
3758static struct hda_verb ad1884a_init_verbs[] = {
3759 /* DACs; unmute as default */
3760 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3761 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3762 /* Port-A (HP) mixer - route only from analog mixer */
3763 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3764 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3765 /* Port-A pin */
3766 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3767 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3768 /* Port-D (Line-out) mixer - route only from analog mixer */
3769 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3770 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3771 /* Port-D pin */
3772 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3773 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3774 /* Mono-out mixer - route only from analog mixer */
3775 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3776 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3777 /* Mono-out pin */
3778 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3779 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3780 /* Port-B (front mic) pin */
3781 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003782 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003783 /* Port-C (rear line-in) pin */
3784 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003785 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003786 /* Port-E (rear mic) pin */
3787 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3788 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3789 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
3790 /* Port-F (CD) pin */
3791 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3792 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3793 /* Analog mixer; mute as default */
3794 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3795 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3796 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3797 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3798 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
3799 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3800 /* Analog Mix output amp */
3801 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3802 /* capture sources */
3803 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
3804 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3805 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3806 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3807 /* SPDIF output amp */
3808 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3809 { } /* end */
3810};
3811
3812#ifdef CONFIG_SND_HDA_POWER_SAVE
3813static struct hda_amp_list ad1884a_loopbacks[] = {
3814 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3815 { 0x20, HDA_INPUT, 1 }, /* Mic */
3816 { 0x20, HDA_INPUT, 2 }, /* CD */
3817 { 0x20, HDA_INPUT, 4 }, /* Docking */
3818 { } /* end */
3819};
3820#endif
3821
3822/*
3823 * Laptop model
3824 *
3825 * Port A: Headphone jack
3826 * Port B: MIC jack
3827 * Port C: Internal MIC
3828 * Port D: Dock Line Out (if enabled)
3829 * Port E: Dock Line In (if enabled)
3830 * Port F: Internal speakers
3831 */
3832
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003833static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
3834 struct snd_ctl_elem_value *ucontrol)
3835{
3836 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3837 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
3838 int mute = (!ucontrol->value.integer.value[0] &&
3839 !ucontrol->value.integer.value[1]);
3840 /* toggle GPIO1 according to the mute state */
3841 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
3842 mute ? 0x02 : 0x0);
3843 return ret;
3844}
Takashi Iwaic5059252008-02-16 09:43:56 +01003845
3846static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
3847 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003848 {
3849 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3850 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003851 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003852 .info = snd_hda_mixer_amp_switch_info,
3853 .get = snd_hda_mixer_amp_switch_get,
3854 .put = ad1884a_mobile_master_sw_put,
3855 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3856 },
Takashi Iwaic5059252008-02-16 09:43:56 +01003857 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3858 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3859 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3860 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3861 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3862 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3863 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3864 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3865 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003866 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3867 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
3868 HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3869 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3870 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003871 { } /* end */
3872};
3873
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003874static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
3875 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db172009-07-02 16:10:23 +02003876 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
3877 {
3878 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3879 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003880 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai099db172009-07-02 16:10:23 +02003881 .info = snd_hda_mixer_amp_switch_info,
3882 .get = snd_hda_mixer_amp_switch_get,
3883 .put = ad1884a_mobile_master_sw_put,
3884 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3885 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003886 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3887 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02003888 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
3889 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003890 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3891 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003892 { } /* end */
3893};
3894
Takashi Iwaic5059252008-02-16 09:43:56 +01003895/* mute internal speaker if HP is plugged */
3896static void ad1884a_hp_automute(struct hda_codec *codec)
3897{
3898 unsigned int present;
3899
Takashi Iwaid56757a2009-11-18 08:00:14 +01003900 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01003901 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3902 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3903 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3904 present ? 0x00 : 0x02);
3905}
3906
Takashi Iwai269ef192008-05-30 15:32:15 +02003907/* switch to external mic if plugged */
3908static void ad1884a_hp_automic(struct hda_codec *codec)
3909{
3910 unsigned int present;
3911
Takashi Iwaid56757a2009-11-18 08:00:14 +01003912 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02003913 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
3914 present ? 0 : 1);
3915}
3916
Takashi Iwaic5059252008-02-16 09:43:56 +01003917#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02003918#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01003919
3920/* unsolicited event for HP jack sensing */
3921static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
3922{
Takashi Iwai269ef192008-05-30 15:32:15 +02003923 switch (res >> 26) {
3924 case AD1884A_HP_EVENT:
3925 ad1884a_hp_automute(codec);
3926 break;
3927 case AD1884A_MIC_EVENT:
3928 ad1884a_hp_automic(codec);
3929 break;
3930 }
Takashi Iwaic5059252008-02-16 09:43:56 +01003931}
3932
3933/* initialize jack-sensing, too */
3934static int ad1884a_hp_init(struct hda_codec *codec)
3935{
3936 ad198x_init(codec);
3937 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02003938 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01003939 return 0;
3940}
3941
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003942/* mute internal speaker if HP or docking HP is plugged */
3943static void ad1884a_laptop_automute(struct hda_codec *codec)
3944{
3945 unsigned int present;
3946
Takashi Iwaid56757a2009-11-18 08:00:14 +01003947 present = snd_hda_jack_detect(codec, 0x11);
3948 if (!present)
3949 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003950 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3951 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3952 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3953 present ? 0x00 : 0x02);
3954}
3955
3956/* switch to external mic if plugged */
3957static void ad1884a_laptop_automic(struct hda_codec *codec)
3958{
3959 unsigned int idx;
3960
Takashi Iwaid56757a2009-11-18 08:00:14 +01003961 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003962 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01003963 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003964 idx = 4;
3965 else
3966 idx = 1;
3967 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
3968}
3969
3970/* unsolicited event for HP jack sensing */
3971static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
3972 unsigned int res)
3973{
3974 switch (res >> 26) {
3975 case AD1884A_HP_EVENT:
3976 ad1884a_laptop_automute(codec);
3977 break;
3978 case AD1884A_MIC_EVENT:
3979 ad1884a_laptop_automic(codec);
3980 break;
3981 }
3982}
3983
3984/* initialize jack-sensing, too */
3985static int ad1884a_laptop_init(struct hda_codec *codec)
3986{
3987 ad198x_init(codec);
3988 ad1884a_laptop_automute(codec);
3989 ad1884a_laptop_automic(codec);
3990 return 0;
3991}
3992
Takashi Iwaic5059252008-02-16 09:43:56 +01003993/* additional verbs for laptop model */
3994static struct hda_verb ad1884a_laptop_verbs[] = {
3995 /* Port-A (HP) pin - always unmuted */
3996 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3997 /* Port-F (int speaker) mixer - route only from analog mixer */
3998 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3999 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08004000 /* Port-F (int speaker) pin */
4001 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01004002 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08004003 /* required for compaq 6530s/6531s speaker output */
4004 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004005 /* Port-C pin - internal mic-in */
4006 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4007 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4008 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02004009 /* Port-D (docking line-out) pin - default unmuted */
4010 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01004011 /* analog mix */
4012 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4013 /* unsolicited event for pin-sense */
4014 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004015 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004016 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004017 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02004018 /* allow to touch GPIO1 (for mute control) */
4019 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4020 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4021 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01004022 { } /* end */
4023};
4024
Takashi Iwai73156132009-04-23 08:24:48 +02004025static struct hda_verb ad1884a_mobile_verbs[] = {
4026 /* DACs; unmute as default */
4027 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4028 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4029 /* Port-A (HP) mixer - route only from analog mixer */
4030 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4031 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4032 /* Port-A pin */
4033 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4034 /* Port-A (HP) pin - always unmuted */
4035 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4036 /* Port-B (mic jack) pin */
4037 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4038 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4039 /* Port-C (int mic) pin */
4040 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4041 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4042 /* Port-F (int speaker) mixer - route only from analog mixer */
4043 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4044 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4045 /* Port-F pin */
4046 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4047 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4048 /* Analog mixer; mute as default */
4049 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4050 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4051 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4052 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4053 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4054 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4055 /* Analog Mix output amp */
4056 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4057 /* capture sources */
4058 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4059 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4060 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4061 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4062 /* unsolicited event for pin-sense */
4063 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4064 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db172009-07-02 16:10:23 +02004065 /* allow to touch GPIO1 (for mute control) */
4066 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4067 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4068 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02004069 { } /* end */
4070};
4071
Takashi Iwaic5059252008-02-16 09:43:56 +01004072/*
Takashi Iwaif0813742008-03-18 12:13:03 +01004073 * Thinkpad X300
4074 * 0x11 - HP
4075 * 0x12 - speaker
4076 * 0x14 - mic-in
4077 * 0x17 - built-in mic
4078 */
4079
4080static struct hda_verb ad1984a_thinkpad_verbs[] = {
4081 /* HP unmute */
4082 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4083 /* analog mix */
4084 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4085 /* turn on EAPD */
4086 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4087 /* unsolicited event for pin-sense */
4088 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4089 /* internal mic - dmic */
4090 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02004091 /* set magic COEFs for dmic */
4092 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4093 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01004094 { } /* end */
4095};
4096
4097static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
4098 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4099 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4100 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4101 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4102 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4103 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01004104 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
4105 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4106 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4107 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4108 {
4109 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4110 .name = "Capture Source",
4111 .info = ad198x_mux_enum_info,
4112 .get = ad198x_mux_enum_get,
4113 .put = ad198x_mux_enum_put,
4114 },
4115 { } /* end */
4116};
4117
4118static struct hda_input_mux ad1984a_thinkpad_capture_source = {
4119 .num_items = 3,
4120 .items = {
4121 { "Mic", 0x0 },
4122 { "Internal Mic", 0x5 },
4123 { "Mix", 0x3 },
4124 },
4125};
4126
4127/* mute internal speaker if HP is plugged */
4128static void ad1984a_thinkpad_automute(struct hda_codec *codec)
4129{
4130 unsigned int present;
4131
Takashi Iwaid56757a2009-11-18 08:00:14 +01004132 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01004133 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
4134 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4135}
4136
4137/* unsolicited event for HP jack sensing */
4138static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
4139 unsigned int res)
4140{
4141 if ((res >> 26) != AD1884A_HP_EVENT)
4142 return;
4143 ad1984a_thinkpad_automute(codec);
4144}
4145
4146/* initialize jack-sensing, too */
4147static int ad1984a_thinkpad_init(struct hda_codec *codec)
4148{
4149 ad198x_init(codec);
4150 ad1984a_thinkpad_automute(codec);
4151 return 0;
4152}
4153
4154/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004155 * HP Touchsmart
4156 * port-A (0x11) - front hp-out
4157 * port-B (0x14) - unused
4158 * port-C (0x15) - unused
4159 * port-D (0x12) - rear line out
4160 * port-E (0x1c) - front mic-in
4161 * port-F (0x16) - Internal speakers
4162 * digital-mic (0x17) - Internal mic
4163 */
4164
4165static struct hda_verb ad1984a_touchsmart_verbs[] = {
4166 /* DACs; unmute as default */
4167 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4168 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4169 /* Port-A (HP) mixer - route only from analog mixer */
4170 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4171 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4172 /* Port-A pin */
4173 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4174 /* Port-A (HP) pin - always unmuted */
4175 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4176 /* Port-E (int speaker) mixer - route only from analog mixer */
4177 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4178 /* Port-E pin */
4179 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4180 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4181 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4182 /* Port-F (int speaker) mixer - route only from analog mixer */
4183 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4184 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4185 /* Port-F pin */
4186 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4187 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4188 /* Analog mixer; mute as default */
4189 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4190 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4191 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4192 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4193 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4194 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4195 /* Analog Mix output amp */
4196 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4197 /* capture sources */
4198 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4199 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4200 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4201 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4202 /* unsolicited event for pin-sense */
4203 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4204 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4205 /* allow to touch GPIO1 (for mute control) */
4206 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4207 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4208 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4209 /* internal mic - dmic */
4210 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4211 /* set magic COEFs for dmic */
4212 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4213 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4214 { } /* end */
4215};
4216
4217static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
4218 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4219/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4220 {
4221 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004222 .subdevice = HDA_SUBDEV_AMP_FLAG,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004223 .name = "Master Playback Switch",
4224 .info = snd_hda_mixer_amp_switch_info,
4225 .get = snd_hda_mixer_amp_switch_get,
4226 .put = ad1884a_mobile_master_sw_put,
4227 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4228 },
4229 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4230 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4231 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4232 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4233 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
4234 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4235 { } /* end */
4236};
4237
4238/* switch to external mic if plugged */
4239static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4240{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004241 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004242 snd_hda_codec_write(codec, 0x0c, 0,
4243 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004244 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004245 snd_hda_codec_write(codec, 0x0c, 0,
4246 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004247}
4248
4249
4250/* unsolicited event for HP jack sensing */
4251static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4252 unsigned int res)
4253{
4254 switch (res >> 26) {
4255 case AD1884A_HP_EVENT:
4256 ad1884a_hp_automute(codec);
4257 break;
4258 case AD1884A_MIC_EVENT:
4259 ad1984a_touchsmart_automic(codec);
4260 break;
4261 }
4262}
4263
4264/* initialize jack-sensing, too */
4265static int ad1984a_touchsmart_init(struct hda_codec *codec)
4266{
4267 ad198x_init(codec);
4268 ad1884a_hp_automute(codec);
4269 ad1984a_touchsmart_automic(codec);
4270 return 0;
4271}
4272
4273
4274/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004275 */
4276
4277enum {
4278 AD1884A_DESKTOP,
4279 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004280 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004281 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004282 AD1984A_TOUCHSMART,
Takashi Iwaic5059252008-02-16 09:43:56 +01004283 AD1884A_MODELS
4284};
4285
4286static const char *ad1884a_models[AD1884A_MODELS] = {
4287 [AD1884A_DESKTOP] = "desktop",
4288 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004289 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004290 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004291 [AD1984A_TOUCHSMART] = "touchsmart",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004292};
4293
4294static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
4295 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004296 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004297 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004298 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004299 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004300 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4301 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004302 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004303 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004304 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004305 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004306};
4307
4308static int patch_ad1884a(struct hda_codec *codec)
4309{
4310 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004311 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004312
4313 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4314 if (spec == NULL)
4315 return -ENOMEM;
4316
Takashi Iwaic5059252008-02-16 09:43:56 +01004317 codec->spec = spec;
4318
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004319 err = snd_hda_attach_beep_device(codec, 0x10);
4320 if (err < 0) {
4321 ad198x_free(codec);
4322 return err;
4323 }
4324 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4325
Takashi Iwaic5059252008-02-16 09:43:56 +01004326 spec->multiout.max_channels = 2;
4327 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4328 spec->multiout.dac_nids = ad1884a_dac_nids;
4329 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4330 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4331 spec->adc_nids = ad1884a_adc_nids;
4332 spec->capsrc_nids = ad1884a_capsrc_nids;
4333 spec->input_mux = &ad1884a_capture_source;
4334 spec->num_mixers = 1;
4335 spec->mixers[0] = ad1884a_base_mixers;
4336 spec->num_init_verbs = 1;
4337 spec->init_verbs[0] = ad1884a_init_verbs;
4338 spec->spdif_route = 0;
4339#ifdef CONFIG_SND_HDA_POWER_SAVE
4340 spec->loopback.amplist = ad1884a_loopbacks;
4341#endif
4342 codec->patch_ops = ad198x_patch_ops;
4343
4344 /* override some parameters */
4345 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004346 ad1884a_models,
4347 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01004348 switch (board_config) {
4349 case AD1884A_LAPTOP:
4350 spec->mixers[0] = ad1884a_laptop_mixers;
4351 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4352 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004353 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4354 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004355 /* set the upper-limit for mixer amp to 0dB for avoiding the
4356 * possible damage by overloading
4357 */
4358 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4359 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4360 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4361 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4362 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004363 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004364 case AD1884A_MOBILE:
4365 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004366 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004367 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004368 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4369 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004370 /* set the upper-limit for mixer amp to 0dB for avoiding the
4371 * possible damage by overloading
4372 */
4373 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4374 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4375 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4376 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4377 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004378 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004379 case AD1884A_THINKPAD:
4380 spec->mixers[0] = ad1984a_thinkpad_mixers;
4381 spec->init_verbs[spec->num_init_verbs++] =
4382 ad1984a_thinkpad_verbs;
4383 spec->multiout.dig_out_nid = 0;
4384 spec->input_mux = &ad1984a_thinkpad_capture_source;
4385 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4386 codec->patch_ops.init = ad1984a_thinkpad_init;
4387 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004388 case AD1984A_TOUCHSMART:
4389 spec->mixers[0] = ad1984a_touchsmart_mixers;
4390 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4391 spec->multiout.dig_out_nid = 0;
4392 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4393 codec->patch_ops.init = ad1984a_touchsmart_init;
4394 /* set the upper-limit for mixer amp to 0dB for avoiding the
4395 * possible damage by overloading
4396 */
4397 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4398 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4399 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4400 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4401 (1 << AC_AMPCAP_MUTE_SHIFT));
4402 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004403 }
4404
Takashi Iwai729d55b2009-12-25 22:49:01 +01004405 codec->no_trigger_sense = 1;
4406
Takashi Iwaic5059252008-02-16 09:43:56 +01004407 return 0;
4408}
4409
4410
4411/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004412 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004413 *
4414 * port-A - front hp-out
4415 * port-B - front mic-in
4416 * port-C - rear line-in, shared surr-out (3stack)
4417 * port-D - rear line-out
4418 * port-E - rear mic-in, shared clfe-out (3stack)
4419 * port-F - rear surr-out (6stack)
4420 * port-G - rear clfe-out (6stack)
4421 */
4422
4423static hda_nid_t ad1882_dac_nids[3] = {
4424 0x04, 0x03, 0x05
4425};
4426
4427static hda_nid_t ad1882_adc_nids[2] = {
4428 0x08, 0x09,
4429};
4430
4431static hda_nid_t ad1882_capsrc_nids[2] = {
4432 0x0c, 0x0d,
4433};
4434
4435#define AD1882_SPDIF_OUT 0x02
4436
4437/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
4438static struct hda_input_mux ad1882_capture_source = {
4439 .num_items = 5,
4440 .items = {
4441 { "Front Mic", 0x1 },
4442 { "Mic", 0x4 },
4443 { "Line", 0x2 },
4444 { "CD", 0x3 },
4445 { "Mix", 0x7 },
4446 },
4447};
4448
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004449/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
4450static struct hda_input_mux ad1882a_capture_source = {
4451 .num_items = 5,
4452 .items = {
4453 { "Front Mic", 0x1 },
4454 { "Mic", 0x4},
4455 { "Line", 0x2 },
4456 { "Digital Mic", 0x06 },
4457 { "Mix", 0x7 },
4458 },
4459};
4460
Takashi Iwai0ac85512007-06-20 15:46:13 +02004461static struct snd_kcontrol_new ad1882_base_mixers[] = {
4462 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4463 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4464 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4465 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4466 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4467 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4468 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4469 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004470
Takashi Iwai0ac85512007-06-20 15:46:13 +02004471 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
4472 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
4473 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
4474 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4475 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4476 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4477 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4478 {
4479 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4480 /* The multiple "Capture Source" controls confuse alsamixer
4481 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004482 */
4483 /* .name = "Capture Source", */
4484 .name = "Input Source",
4485 .count = 2,
4486 .info = ad198x_mux_enum_info,
4487 .get = ad198x_mux_enum_get,
4488 .put = ad198x_mux_enum_put,
4489 },
4490 /* SPDIF controls */
4491 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4492 {
4493 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4494 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4495 /* identical with ad1983 */
4496 .info = ad1983_spdif_route_info,
4497 .get = ad1983_spdif_route_get,
4498 .put = ad1983_spdif_route_put,
4499 },
4500 { } /* end */
4501};
4502
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004503static struct snd_kcontrol_new ad1882_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, 0x01, HDA_INPUT),
4507 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4508 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4509 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, 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 { } /* end */
4513};
4514
4515static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
4516 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4517 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4518 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4519 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4520 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4521 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4522 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4523 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004524 HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
4525 { } /* end */
4526};
4527
Takashi Iwai0ac85512007-06-20 15:46:13 +02004528static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
4529 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4530 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4531 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4532 {
4533 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4534 .name = "Channel Mode",
4535 .info = ad198x_ch_mode_info,
4536 .get = ad198x_ch_mode_get,
4537 .put = ad198x_ch_mode_put,
4538 },
4539 { } /* end */
4540};
4541
4542static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
4543 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4544 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4545 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4546 { } /* end */
4547};
4548
4549static struct hda_verb ad1882_ch2_init[] = {
4550 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4551 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4552 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4553 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4554 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4555 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4556 { } /* end */
4557};
4558
4559static struct hda_verb ad1882_ch4_init[] = {
4560 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4561 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4562 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4563 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4564 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4565 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4566 { } /* end */
4567};
4568
4569static struct hda_verb ad1882_ch6_init[] = {
4570 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4571 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4572 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4573 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4574 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4575 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4576 { } /* end */
4577};
4578
4579static struct hda_channel_mode ad1882_modes[3] = {
4580 { 2, ad1882_ch2_init },
4581 { 4, ad1882_ch4_init },
4582 { 6, ad1882_ch6_init },
4583};
4584
4585/*
4586 * initialization verbs
4587 */
4588static struct hda_verb ad1882_init_verbs[] = {
4589 /* DACs; mute as default */
4590 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4591 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4592 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4593 /* Port-A (HP) mixer */
4594 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4595 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4596 /* Port-A pin */
4597 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4598 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4599 /* HP selector - select DAC2 */
4600 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4601 /* Port-D (Line-out) mixer */
4602 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4603 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4604 /* Port-D pin */
4605 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4606 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4607 /* Mono-out mixer */
4608 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4609 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4610 /* Mono-out pin */
4611 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4612 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4613 /* Port-B (front mic) pin */
4614 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4615 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4616 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4617 /* Port-C (line-in) pin */
4618 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4619 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4620 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4621 /* Port-C mixer - mute as input */
4622 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4623 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4624 /* Port-E (mic-in) pin */
4625 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4626 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4627 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4628 /* Port-E mixer - mute as input */
4629 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4630 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4631 /* Port-F (surround) */
4632 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4633 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4634 /* Port-G (CLFE) */
4635 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4636 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4637 /* Analog mixer; mute as default */
4638 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4639 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4640 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4641 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4642 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4643 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4644 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4645 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4646 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4647 /* Analog Mix output amp */
4648 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4649 /* SPDIF output selector */
4650 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4651 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4652 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4653 { } /* end */
4654};
4655
Takashi Iwaicb53c622007-08-10 17:21:45 +02004656#ifdef CONFIG_SND_HDA_POWER_SAVE
4657static struct hda_amp_list ad1882_loopbacks[] = {
4658 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4659 { 0x20, HDA_INPUT, 1 }, /* Mic */
4660 { 0x20, HDA_INPUT, 4 }, /* Line */
4661 { 0x20, HDA_INPUT, 6 }, /* CD */
4662 { } /* end */
4663};
4664#endif
4665
Takashi Iwai0ac85512007-06-20 15:46:13 +02004666/* models */
4667enum {
4668 AD1882_3STACK,
4669 AD1882_6STACK,
4670 AD1882_MODELS
4671};
4672
4673static const char *ad1882_models[AD1986A_MODELS] = {
4674 [AD1882_3STACK] = "3stack",
4675 [AD1882_6STACK] = "6stack",
4676};
4677
4678
4679static int patch_ad1882(struct hda_codec *codec)
4680{
4681 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004682 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004683
4684 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4685 if (spec == NULL)
4686 return -ENOMEM;
4687
Takashi Iwai0ac85512007-06-20 15:46:13 +02004688 codec->spec = spec;
4689
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004690 err = snd_hda_attach_beep_device(codec, 0x10);
4691 if (err < 0) {
4692 ad198x_free(codec);
4693 return err;
4694 }
4695 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4696
Takashi Iwai0ac85512007-06-20 15:46:13 +02004697 spec->multiout.max_channels = 6;
4698 spec->multiout.num_dacs = 3;
4699 spec->multiout.dac_nids = ad1882_dac_nids;
4700 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
4701 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
4702 spec->adc_nids = ad1882_adc_nids;
4703 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004704 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004705 spec->input_mux = &ad1882_capture_source;
4706 else
4707 spec->input_mux = &ad1882a_capture_source;
4708 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004709 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004710 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004711 spec->mixers[1] = ad1882_loopback_mixers;
4712 else
4713 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004714 spec->num_init_verbs = 1;
4715 spec->init_verbs[0] = ad1882_init_verbs;
4716 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02004717#ifdef CONFIG_SND_HDA_POWER_SAVE
4718 spec->loopback.amplist = ad1882_loopbacks;
4719#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01004720 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004721
4722 codec->patch_ops = ad198x_patch_ops;
4723
4724 /* override some parameters */
4725 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
4726 ad1882_models, NULL);
4727 switch (board_config) {
4728 default:
4729 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004730 spec->num_mixers = 3;
4731 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004732 spec->channel_mode = ad1882_modes;
4733 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
4734 spec->need_dac_fix = 1;
4735 spec->multiout.max_channels = 2;
4736 spec->multiout.num_dacs = 1;
4737 break;
4738 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004739 spec->num_mixers = 3;
4740 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004741 break;
4742 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01004743
4744 codec->no_trigger_sense = 1;
4745
Takashi Iwai0ac85512007-06-20 15:46:13 +02004746 return 0;
4747}
4748
4749
4750/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004751 * patch entries
4752 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004753static struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004754 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02004755 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004756 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004757 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004758 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
4759 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02004760 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
4761 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004762 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004763 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01004764 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02004765 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004766 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02004767 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
4768 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004769 {} /* terminator */
4770};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004771
4772MODULE_ALIAS("snd-hda-codec-id:11d4*");
4773
4774MODULE_LICENSE("GPL");
4775MODULE_DESCRIPTION("Analog Devices HD-audio codec");
4776
4777static struct hda_codec_preset_list analog_list = {
4778 .preset = snd_hda_preset_analog,
4779 .owner = THIS_MODULE,
4780};
4781
4782static int __init patch_analog_init(void)
4783{
4784 return snd_hda_add_codec_preset(&analog_list);
4785}
4786
4787static void __exit patch_analog_exit(void)
4788{
4789 snd_hda_delete_codec_preset(&analog_list);
4790}
4791
4792module_init(patch_analog_init)
4793module_exit(patch_analog_exit)