blob: 5e2bb181a1491a0c41305c3217c7afbfa79a244e [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Takashi Iwai0ac85512007-06-20 15:46:13 +02002 * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
3 * AD1986A, AD1988
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
Takashi Iwai2bac6472007-05-18 18:21:41 +02005 * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 *
7 * This driver is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This driver is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/init.h>
23#include <linux/delay.h>
24#include <linux/slab.h>
25#include <linux/pci.h>
Ingo Molnar62932df2006-01-16 16:34:20 +010026
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <sound/core.h>
28#include "hda_codec.h"
29#include "hda_local.h"
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010030#include "hda_beep.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020032struct ad198x_spec {
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010033 struct snd_kcontrol_new *mixers[5];
Takashi Iwai985be542005-11-02 18:26:49 +010034 int num_mixers;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010035 unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
Takashi Iwaid32410b12005-11-24 16:06:23 +010036 const struct hda_verb *init_verbs[5]; /* initialization verbs
Takashi Iwai985be542005-11-02 18:26:49 +010037 * don't forget NULL termination!
38 */
39 unsigned int num_init_verbs;
40
41 /* playback */
42 struct hda_multi_out multiout; /* playback set-up
43 * max_channels, dacs must be set
44 * dig_out_nid and hp_nid are optional
45 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +010046 unsigned int cur_eapd;
Takashi Iwai2125cad2006-03-27 12:52:22 +020047 unsigned int need_dac_fix;
Takashi Iwai985be542005-11-02 18:26:49 +010048
49 /* capture */
50 unsigned int num_adc_nids;
51 hda_nid_t *adc_nids;
52 hda_nid_t dig_in_nid; /* digital-in NID; optional */
53
54 /* capture source */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020055 const struct hda_input_mux *input_mux;
Takashi Iwai2e5b9562005-11-21 16:36:15 +010056 hda_nid_t *capsrc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010057 unsigned int cur_mux[3];
58
59 /* channel model */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +010060 const struct hda_channel_mode *channel_mode;
Takashi Iwai985be542005-11-02 18:26:49 +010061 int num_channel_mode;
62
63 /* PCM information */
Takashi Iwai2bac6472007-05-18 18:21:41 +020064 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
Takashi Iwai985be542005-11-02 18:26:49 +010065
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020066 unsigned int spdif_route;
Takashi Iwaid32410b12005-11-24 16:06:23 +010067
68 /* dynamic controls, init_verbs and input_mux */
69 struct auto_pin_cfg autocfg;
Takashi Iwai603c4012008-07-30 15:01:44 +020070 struct snd_array kctls;
Takashi Iwaid32410b12005-11-24 16:06:23 +010071 struct hda_input_mux private_imux;
Takashi Iwai41923e42007-10-22 17:20:10 +020072 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
Takashi Iwaicb53c622007-08-10 17:21:45 +020073
Takashi Iwai8ab78c72007-09-06 14:29:53 +020074 unsigned int jack_present :1;
Takashi Iwaiee6e3652009-12-08 17:23:33 +010075 unsigned int inv_jack_detect:1; /* inverted jack-detection */
76 unsigned int inv_eapd:1; /* inverted EAPD implementation */
Takashi Iwai8ab78c72007-09-06 14:29:53 +020077
Takashi Iwaicb53c622007-08-10 17:21:45 +020078#ifdef CONFIG_SND_HDA_POWER_SAVE
79 struct hda_loopback_check loopback;
80#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +010081 /* for virtual master */
82 hda_nid_t vmaster_nid;
Takashi Iwai2134ea42008-01-10 16:53:55 +010083 const char **slave_vols;
84 const char **slave_sws;
Linus Torvalds1da177e2005-04-16 15:20:36 -070085};
86
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020087/*
88 * input MUX handling (common part)
89 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010090static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020091{
92 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
93 struct ad198x_spec *spec = codec->spec;
94
95 return snd_hda_input_mux_info(spec->input_mux, uinfo);
96}
97
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010098static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020099{
100 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
101 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100102 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200103
Takashi Iwai985be542005-11-02 18:26:49 +0100104 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200105 return 0;
106}
107
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100108static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200109{
110 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
111 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100112 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200113
114 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai2e5b9562005-11-21 16:36:15 +0100115 spec->capsrc_nids[adc_idx],
116 &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200117}
118
119/*
120 * initialization (common callbacks)
121 */
122static int ad198x_init(struct hda_codec *codec)
123{
124 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100125 int i;
126
127 for (i = 0; i < spec->num_init_verbs; i++)
128 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200129 return 0;
130}
131
Takashi Iwai2134ea42008-01-10 16:53:55 +0100132static const char *ad_slave_vols[] = {
133 "Front Playback Volume",
134 "Surround Playback Volume",
135 "Center Playback Volume",
136 "LFE Playback Volume",
137 "Side Playback Volume",
138 "Headphone Playback Volume",
139 "Mono Playback Volume",
Takashi Iwai628ed132008-01-25 11:56:57 +0100140 "Speaker Playback Volume",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100141 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100142 NULL
143};
144
145static const char *ad_slave_sws[] = {
146 "Front Playback Switch",
147 "Surround Playback Switch",
148 "Center Playback Switch",
149 "LFE Playback Switch",
150 "Side Playback Switch",
151 "Headphone Playback Switch",
152 "Mono Playback Switch",
Takashi Iwai628ed132008-01-25 11:56:57 +0100153 "Speaker Playback Switch",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100154 "IEC958 Playback Switch",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100155 NULL
156};
157
Takashi Iwai603c4012008-07-30 15:01:44 +0200158static void ad198x_free_kctls(struct hda_codec *codec);
159
Takashi Iwai67d634c2009-11-16 15:35:59 +0100160#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100161/* additional beep mixers; the actual parameters are overwritten at build */
162static struct snd_kcontrol_new ad_beep_mixer[] = {
163 HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
Jaroslav Kysela123c07a2009-10-21 14:48:23 +0200164 HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100165 { } /* end */
166};
167
168#define set_beep_amp(spec, nid, idx, dir) \
169 ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100170#else
171#define set_beep_amp(spec, nid, idx, dir) /* NOP */
172#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100173
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200174static int ad198x_build_controls(struct hda_codec *codec)
175{
176 struct ad198x_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100177 struct snd_kcontrol *kctl;
Takashi Iwai985be542005-11-02 18:26:49 +0100178 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200179 int err;
180
Takashi Iwai985be542005-11-02 18:26:49 +0100181 for (i = 0; i < spec->num_mixers; i++) {
182 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
183 if (err < 0)
184 return err;
185 }
186 if (spec->multiout.dig_out_nid) {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200187 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100188 if (err < 0)
189 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100190 err = snd_hda_create_spdif_share_sw(codec,
191 &spec->multiout);
192 if (err < 0)
193 return err;
194 spec->multiout.share_spdif = 1;
Takashi Iwai985be542005-11-02 18:26:49 +0100195 }
196 if (spec->dig_in_nid) {
197 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
198 if (err < 0)
199 return err;
200 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100201
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100202 /* create beep controls if needed */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100203#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100204 if (spec->beep_amp) {
205 struct snd_kcontrol_new *knew;
206 for (knew = ad_beep_mixer; knew->name; knew++) {
207 struct snd_kcontrol *kctl;
208 kctl = snd_ctl_new1(knew, codec);
209 if (!kctl)
210 return -ENOMEM;
211 kctl->private_value = spec->beep_amp;
Jaroslav Kysela3911a4c2009-11-11 13:43:01 +0100212 err = snd_hda_ctl_add(codec,
213 get_amp_nid_(spec->beep_amp),
214 kctl);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100215 if (err < 0)
216 return err;
217 }
218 }
Takashi Iwai67d634c2009-11-16 15:35:59 +0100219#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100220
Takashi Iwai2134ea42008-01-10 16:53:55 +0100221 /* if we have no master control, let's create it */
222 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100223 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100224 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100225 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100226 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100227 vmaster_tlv,
Takashi Iwai2134ea42008-01-10 16:53:55 +0100228 (spec->slave_vols ?
229 spec->slave_vols : ad_slave_vols));
230 if (err < 0)
231 return err;
232 }
233 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
234 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
235 NULL,
236 (spec->slave_sws ?
237 spec->slave_sws : ad_slave_sws));
238 if (err < 0)
239 return err;
240 }
241
Takashi Iwai603c4012008-07-30 15:01:44 +0200242 ad198x_free_kctls(codec); /* no longer needed */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100243
244 /* assign Capture Source enums to NID */
245 kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
246 if (!kctl)
247 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
248 for (i = 0; kctl && i < kctl->count; i++) {
249 err = snd_hda_add_nids(codec, kctl, i, spec->capsrc_nids,
250 spec->input_mux->num_items);
251 if (err < 0)
252 return err;
253 }
254
255 /* assign IEC958 enums to NID */
256 kctl = snd_hda_find_mixer_ctl(codec,
257 SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
258 if (kctl) {
259 err = snd_hda_add_nid(codec, kctl, 0,
260 spec->multiout.dig_out_nid);
261 if (err < 0)
262 return err;
263 }
264
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200265 return 0;
266}
267
Takashi Iwaicb53c622007-08-10 17:21:45 +0200268#ifdef CONFIG_SND_HDA_POWER_SAVE
269static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
270{
271 struct ad198x_spec *spec = codec->spec;
272 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
273}
274#endif
275
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200276/*
277 * Analog playback callbacks
278 */
279static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
280 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100281 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200282{
283 struct ad198x_spec *spec = codec->spec;
Takashi Iwai9a081602008-02-12 18:37:26 +0100284 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
285 hinfo);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200286}
287
288static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
289 struct hda_codec *codec,
290 unsigned int stream_tag,
291 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100292 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200293{
294 struct ad198x_spec *spec = codec->spec;
295 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
296 format, substream);
297}
298
299static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
300 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100301 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200302{
303 struct ad198x_spec *spec = codec->spec;
304 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
305}
306
307/*
308 * Digital out
309 */
310static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
311 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100312 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200313{
314 struct ad198x_spec *spec = codec->spec;
315 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
316}
317
318static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
319 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100320 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200321{
322 struct ad198x_spec *spec = codec->spec;
323 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
324}
325
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200326static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
327 struct hda_codec *codec,
328 unsigned int stream_tag,
329 unsigned int format,
330 struct snd_pcm_substream *substream)
331{
332 struct ad198x_spec *spec = codec->spec;
333 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
334 format, substream);
335}
336
Takashi Iwai9411e212009-02-13 11:32:28 +0100337static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
338 struct hda_codec *codec,
339 struct snd_pcm_substream *substream)
340{
341 struct ad198x_spec *spec = codec->spec;
342 return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
343}
344
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200345/*
346 * Analog capture
347 */
348static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
349 struct hda_codec *codec,
350 unsigned int stream_tag,
351 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100352 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200353{
354 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100355 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
356 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200357 return 0;
358}
359
360static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
361 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100362 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200363{
364 struct ad198x_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +0100365 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200366 return 0;
367}
368
369
370/*
371 */
372static struct hda_pcm_stream ad198x_pcm_analog_playback = {
373 .substreams = 1,
374 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100375 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200376 .nid = 0, /* fill later */
377 .ops = {
378 .open = ad198x_playback_pcm_open,
379 .prepare = ad198x_playback_pcm_prepare,
380 .cleanup = ad198x_playback_pcm_cleanup
381 },
382};
383
384static struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100385 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200386 .channels_min = 2,
387 .channels_max = 2,
388 .nid = 0, /* fill later */
389 .ops = {
390 .prepare = ad198x_capture_pcm_prepare,
391 .cleanup = ad198x_capture_pcm_cleanup
392 },
393};
394
395static struct hda_pcm_stream ad198x_pcm_digital_playback = {
396 .substreams = 1,
397 .channels_min = 2,
398 .channels_max = 2,
399 .nid = 0, /* fill later */
400 .ops = {
401 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200402 .close = ad198x_dig_playback_pcm_close,
Takashi Iwai9411e212009-02-13 11:32:28 +0100403 .prepare = ad198x_dig_playback_pcm_prepare,
404 .cleanup = ad198x_dig_playback_pcm_cleanup
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200405 },
406};
407
Takashi Iwai985be542005-11-02 18:26:49 +0100408static struct hda_pcm_stream ad198x_pcm_digital_capture = {
409 .substreams = 1,
410 .channels_min = 2,
411 .channels_max = 2,
412 /* NID is set in alc_build_pcms */
413};
414
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200415static int ad198x_build_pcms(struct hda_codec *codec)
416{
417 struct ad198x_spec *spec = codec->spec;
418 struct hda_pcm *info = spec->pcm_rec;
419
420 codec->num_pcms = 1;
421 codec->pcm_info = info;
422
423 info->name = "AD198x Analog";
424 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
425 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
426 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
427 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100428 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
429 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200430
431 if (spec->multiout.dig_out_nid) {
432 info++;
433 codec->num_pcms++;
434 info->name = "AD198x Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +0100435 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200436 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
437 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100438 if (spec->dig_in_nid) {
439 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
440 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
441 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200442 }
443
444 return 0;
445}
446
Takashi Iwai603c4012008-07-30 15:01:44 +0200447static void ad198x_free_kctls(struct hda_codec *codec)
448{
449 struct ad198x_spec *spec = codec->spec;
450
451 if (spec->kctls.list) {
452 struct snd_kcontrol_new *kctl = spec->kctls.list;
453 int i;
454 for (i = 0; i < spec->kctls.used; i++)
455 kfree(kctl[i].name);
456 }
457 snd_array_free(&spec->kctls);
458}
459
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200460static void ad198x_free(struct hda_codec *codec)
461{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100462 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +0100463
Takashi Iwai603c4012008-07-30 15:01:44 +0200464 if (!spec)
465 return;
466
467 ad198x_free_kctls(codec);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100468 kfree(spec);
469 snd_hda_detach_beep_device(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200470}
471
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200472static struct hda_codec_ops ad198x_patch_ops = {
473 .build_controls = ad198x_build_controls,
474 .build_pcms = ad198x_build_pcms,
475 .init = ad198x_init,
476 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200477#ifdef CONFIG_SND_HDA_POWER_SAVE
478 .check_power_status = ad198x_check_power_status,
479#endif
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200480};
481
482
483/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100484 * EAPD control
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100485 * the private value = nid
Takashi Iwai18a815d2006-03-01 19:54:39 +0100486 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200487#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100488
489static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
490 struct snd_ctl_elem_value *ucontrol)
491{
492 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
493 struct ad198x_spec *spec = codec->spec;
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100494 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100495 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
496 else
497 ucontrol->value.integer.value[0] = spec->cur_eapd;
498 return 0;
499}
500
501static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
502 struct snd_ctl_elem_value *ucontrol)
503{
504 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
505 struct ad198x_spec *spec = codec->spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +0100506 hda_nid_t nid = kcontrol->private_value & 0xff;
507 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100508 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100509 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100510 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200511 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100512 return 0;
513 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200514 snd_hda_codec_write_cache(codec, nid,
515 0, AC_VERB_SET_EAPD_BTLENABLE,
516 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100517 return 1;
518}
519
Takashi Iwai9230d212006-03-13 13:49:49 +0100520static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
521 struct snd_ctl_elem_info *uinfo);
522static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
523 struct snd_ctl_elem_value *ucontrol);
524static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
525 struct snd_ctl_elem_value *ucontrol);
526
527
Takashi Iwai18a815d2006-03-01 19:54:39 +0100528/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200529 * AD1986A specific
530 */
531
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532#define AD1986A_SPDIF_OUT 0x02
533#define AD1986A_FRONT_DAC 0x03
534#define AD1986A_SURR_DAC 0x04
535#define AD1986A_CLFE_DAC 0x05
536#define AD1986A_ADC 0x06
537
538static hda_nid_t ad1986a_dac_nids[3] = {
539 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
540};
Takashi Iwai985be542005-11-02 18:26:49 +0100541static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +0100542static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543
544static struct hda_input_mux ad1986a_capture_source = {
545 .num_items = 7,
546 .items = {
547 { "Mic", 0x0 },
548 { "CD", 0x1 },
549 { "Aux", 0x3 },
550 { "Line", 0x4 },
551 { "Mix", 0x5 },
552 { "Mono", 0x6 },
553 { "Phone", 0x7 },
554 },
555};
556
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557
Takashi Iwai532d5382007-07-27 19:02:40 +0200558static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
559 .ops = &snd_hda_bind_vol,
560 .values = {
561 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
562 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
563 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
564 0
565 },
566};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
Takashi Iwai532d5382007-07-27 19:02:40 +0200568static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
569 .ops = &snd_hda_bind_sw,
570 .values = {
571 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
572 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
573 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
574 0
575 },
576};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
578/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 * mixers
580 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100581static struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200582 /*
583 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
584 */
585 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
586 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
588 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
589 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
590 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
591 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
592 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
593 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
594 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
595 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
596 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
597 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
598 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
599 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
600 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
601 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
602 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
603 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
604 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100605 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
607 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
608 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
609 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
610 {
611 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
612 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200613 .info = ad198x_mux_enum_info,
614 .get = ad198x_mux_enum_get,
615 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 },
617 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
618 { } /* end */
619};
620
Takashi Iwai9230d212006-03-13 13:49:49 +0100621/* additional mixers for 3stack mode */
622static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
623 {
624 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
625 .name = "Channel Mode",
626 .info = ad198x_ch_mode_info,
627 .get = ad198x_ch_mode_get,
628 .put = ad198x_ch_mode_put,
629 },
630 { } /* end */
631};
632
633/* laptop model - 2ch only */
634static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
635
Takashi Iwai20a45e82007-08-15 22:20:45 +0200636/* master controls both pins 0x1a and 0x1b */
637static struct hda_bind_ctls ad1986a_laptop_master_vol = {
638 .ops = &snd_hda_bind_vol,
639 .values = {
640 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
641 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
642 0,
643 },
644};
645
646static struct hda_bind_ctls ad1986a_laptop_master_sw = {
647 .ops = &snd_hda_bind_sw,
648 .values = {
649 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
650 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
651 0,
652 },
653};
654
Takashi Iwai9230d212006-03-13 13:49:49 +0100655static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
656 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
657 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200658 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
659 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100660 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
661 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
662 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
663 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
664 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
665 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
666 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
667 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100668 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100669 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100670 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
671 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
672 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
673 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
674 {
675 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
676 .name = "Capture Source",
677 .info = ad198x_mux_enum_info,
678 .get = ad198x_mux_enum_get,
679 .put = ad198x_mux_enum_put,
680 },
681 { } /* end */
682};
683
Takashi Iwai825aa972006-03-17 10:50:49 +0100684/* laptop-eapd model - 2ch only */
685
Takashi Iwai825aa972006-03-17 10:50:49 +0100686static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
687 .num_items = 3,
688 .items = {
689 { "Mic", 0x0 },
690 { "Internal Mic", 0x4 },
691 { "Mix", 0x5 },
692 },
693};
694
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100695static struct hda_input_mux ad1986a_automic_capture_source = {
696 .num_items = 2,
697 .items = {
698 { "Mic", 0x0 },
699 { "Mix", 0x5 },
700 },
701};
702
Takashi Iwai16d11a82009-06-24 14:07:53 +0200703static struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200704 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
705 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai16d11a82009-06-24 14:07:53 +0200706 { } /* end */
707};
708
709static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai825aa972006-03-17 10:50:49 +0100710 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
711 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100712 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
713 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
714 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
715 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
716 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
717 {
718 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
719 .name = "Capture Source",
720 .info = ad198x_mux_enum_info,
721 .get = ad198x_mux_enum_get,
722 .put = ad198x_mux_enum_put,
723 },
724 {
725 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
726 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100727 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwai1725b822008-11-21 02:25:48 +0100728 .info = ad198x_eapd_info,
729 .get = ad198x_eapd_get,
730 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100731 .private_value = 0x1b, /* port-D */
Takashi Iwai1725b822008-11-21 02:25:48 +0100732 },
733 { } /* end */
734};
735
Takashi Iwai16d11a82009-06-24 14:07:53 +0200736static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
737 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
738 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
Takashi Iwai825aa972006-03-17 10:50:49 +0100739 { } /* end */
740};
741
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100742/* re-connect the mic boost input according to the jack sensing */
743static void ad1986a_automic(struct hda_codec *codec)
744{
745 unsigned int present;
Takashi Iwaid56757a2009-11-18 08:00:14 +0100746 present = snd_hda_jack_detect(codec, 0x1f);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100747 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
748 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
Takashi Iwaid56757a2009-11-18 08:00:14 +0100749 present ? 0 : 2);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100750}
751
752#define AD1986A_MIC_EVENT 0x36
753
754static void ad1986a_automic_unsol_event(struct hda_codec *codec,
755 unsigned int res)
756{
757 if ((res >> 26) != AD1986A_MIC_EVENT)
758 return;
759 ad1986a_automic(codec);
760}
761
762static int ad1986a_automic_init(struct hda_codec *codec)
763{
764 ad198x_init(codec);
765 ad1986a_automic(codec);
766 return 0;
767}
768
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200769/* laptop-automute - 2ch only */
770
771static void ad1986a_update_hp(struct hda_codec *codec)
772{
773 struct ad198x_spec *spec = codec->spec;
774 unsigned int mute;
775
776 if (spec->jack_present)
777 mute = HDA_AMP_MUTE; /* mute internal speaker */
778 else
779 /* unmute internal speaker if necessary */
780 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
781 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
782 HDA_AMP_MUTE, mute);
783}
784
785static void ad1986a_hp_automute(struct hda_codec *codec)
786{
787 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200788
Takashi Iwaid56757a2009-11-18 08:00:14 +0100789 spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
Takashi Iwai03c405a2009-06-24 14:10:15 +0200790 if (spec->inv_jack_detect)
791 spec->jack_present = !spec->jack_present;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200792 ad1986a_update_hp(codec);
793}
794
795#define AD1986A_HP_EVENT 0x37
796
797static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
798{
799 if ((res >> 26) != AD1986A_HP_EVENT)
800 return;
801 ad1986a_hp_automute(codec);
802}
803
804static int ad1986a_hp_init(struct hda_codec *codec)
805{
806 ad198x_init(codec);
807 ad1986a_hp_automute(codec);
808 return 0;
809}
810
811/* bind hp and internal speaker mute (with plug check) */
812static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
813 struct snd_ctl_elem_value *ucontrol)
814{
815 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
816 long *valp = ucontrol->value.integer.value;
817 int change;
818
819 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
820 HDA_AMP_MUTE,
821 valp[0] ? 0 : HDA_AMP_MUTE);
822 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
823 HDA_AMP_MUTE,
824 valp[1] ? 0 : HDA_AMP_MUTE);
825 if (change)
826 ad1986a_update_hp(codec);
827 return change;
828}
829
Takashi Iwai16d11a82009-06-24 14:07:53 +0200830static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200831 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
832 {
833 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
834 .name = "Master Playback Switch",
Jaroslav Kysela9e3fd872009-12-08 17:45:25 +0100835 .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x1a,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200836 .info = snd_hda_mixer_amp_switch_info,
837 .get = snd_hda_mixer_amp_switch_get,
838 .put = ad1986a_hp_master_sw_put,
839 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
840 },
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200841 { } /* end */
842};
843
Takashi Iwai16d11a82009-06-24 14:07:53 +0200844
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845/*
846 * initialization verbs
847 */
848static struct hda_verb ad1986a_init_verbs[] = {
849 /* Front, Surround, CLFE DAC; mute as default */
850 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
851 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
852 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
853 /* Downmix - off */
854 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
855 /* HP, Line-Out, Surround, CLFE selectors */
856 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
857 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
858 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
859 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
860 /* Mono selector */
861 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
862 /* Mic selector: Mic 1/2 pin */
863 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
864 /* Line-in selector: Line-in */
865 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
866 /* Mic 1/2 swap */
867 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
868 /* Record selector: mic */
869 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
870 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
871 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
872 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
873 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
874 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
875 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
876 /* PC beep */
877 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
878 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
879 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
880 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
881 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
882 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
883 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200884 /* HP Pin */
885 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
886 /* Front, Surround, CLFE Pins */
887 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
888 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
889 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
890 /* Mono Pin */
891 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
892 /* Mic Pin */
893 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
894 /* Line, Aux, CD, Beep-In Pin */
895 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
896 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
897 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
898 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
899 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 { } /* end */
901};
902
Takashi Iwai9230d212006-03-13 13:49:49 +0100903static struct hda_verb ad1986a_ch2_init[] = {
904 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200905 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
906 /* Line-in selectors */
907 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100908 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200909 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
910 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
911 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100912 { } /* end */
913};
914
915static struct hda_verb ad1986a_ch4_init[] = {
916 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200917 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
918 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100919 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200920 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
921 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100922 { } /* end */
923};
924
925static struct hda_verb ad1986a_ch6_init[] = {
926 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200927 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
928 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100929 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200930 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
931 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100932 { } /* end */
933};
934
935static struct hda_channel_mode ad1986a_modes[3] = {
936 { 2, ad1986a_ch2_init },
937 { 4, ad1986a_ch4_init },
938 { 6, ad1986a_ch6_init },
939};
940
Takashi Iwai825aa972006-03-17 10:50:49 +0100941/* eapd initialization */
942static struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +0100943 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa972006-03-17 10:50:49 +0100944 {}
945};
946
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100947static struct hda_verb ad1986a_automic_verbs[] = {
948 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
949 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
950 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
951 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
952 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
953 {}
954};
955
Tobin Davisf36090f2007-01-08 11:07:12 +0100956/* Ultra initialization */
957static struct hda_verb ad1986a_ultra_init[] = {
958 /* eapd initialization */
959 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
960 /* CLFE -> Mic in */
961 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
962 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
963 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
964 { } /* end */
965};
966
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200967/* pin sensing on HP jack */
968static struct hda_verb ad1986a_hp_init_verbs[] = {
969 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
970 {}
971};
972
Takashi Iwaic912e7a2009-06-24 14:14:34 +0200973static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
974 unsigned int res)
975{
976 switch (res >> 26) {
977 case AD1986A_HP_EVENT:
978 ad1986a_hp_automute(codec);
979 break;
980 case AD1986A_MIC_EVENT:
981 ad1986a_automic(codec);
982 break;
983 }
984}
985
986static int ad1986a_samsung_p50_init(struct hda_codec *codec)
987{
988 ad198x_init(codec);
989 ad1986a_hp_automute(codec);
990 ad1986a_automic(codec);
991 return 0;
992}
993
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200994
Takashi Iwai9230d212006-03-13 13:49:49 +0100995/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100996enum {
997 AD1986A_6STACK,
998 AD1986A_3STACK,
999 AD1986A_LAPTOP,
1000 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001001 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +01001002 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +01001003 AD1986A_SAMSUNG,
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001004 AD1986A_SAMSUNG_P50,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001005 AD1986A_MODELS
1006};
Takashi Iwai9230d212006-03-13 13:49:49 +01001007
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001008static const char *ad1986a_models[AD1986A_MODELS] = {
1009 [AD1986A_6STACK] = "6stack",
1010 [AD1986A_3STACK] = "3stack",
1011 [AD1986A_LAPTOP] = "laptop",
1012 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001013 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +01001014 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +01001015 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001016 [AD1986A_SAMSUNG_P50] = "samsung-p50",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001017};
1018
1019static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
1020 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001021 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001022 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001023 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001024 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1025 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1026 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1027 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001028 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001029 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001030 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1031 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1032 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1033 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1034 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001035 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Takashi Iwai7db756f2007-12-24 14:36:09 +01001036 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
Tobin Davis18768992007-03-12 22:20:51 +01001037 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001038 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001039 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
Tobin Davisf36090f2007-01-08 11:07:12 +01001040 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001041 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001042 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001043 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001044 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001045 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001046 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001047 {}
1048};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049
Takashi Iwaicb53c622007-08-10 17:21:45 +02001050#ifdef CONFIG_SND_HDA_POWER_SAVE
1051static struct hda_amp_list ad1986a_loopbacks[] = {
1052 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1053 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1054 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1055 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1056 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1057 { } /* end */
1058};
1059#endif
1060
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001061static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1062{
Takashi Iwai2f334f92009-02-20 14:37:42 +01001063 unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001064 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1065}
1066
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067static int patch_ad1986a(struct hda_codec *codec)
1068{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001069 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001070 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001072 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 if (spec == NULL)
1074 return -ENOMEM;
1075
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 codec->spec = spec;
1077
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001078 err = snd_hda_attach_beep_device(codec, 0x19);
1079 if (err < 0) {
1080 ad198x_free(codec);
1081 return err;
1082 }
1083 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1084
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 spec->multiout.max_channels = 6;
1086 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1087 spec->multiout.dac_nids = ad1986a_dac_nids;
1088 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001089 spec->num_adc_nids = 1;
1090 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001091 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001092 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001093 spec->num_mixers = 1;
1094 spec->mixers[0] = ad1986a_mixers;
1095 spec->num_init_verbs = 1;
1096 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001097#ifdef CONFIG_SND_HDA_POWER_SAVE
1098 spec->loopback.amplist = ad1986a_loopbacks;
1099#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001100 spec->vmaster_nid = 0x1b;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01001101 spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001103 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104
Takashi Iwai9230d212006-03-13 13:49:49 +01001105 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001106 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1107 ad1986a_models,
1108 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001109 switch (board_config) {
1110 case AD1986A_3STACK:
1111 spec->num_mixers = 2;
1112 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001113 spec->num_init_verbs = 2;
1114 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001115 spec->channel_mode = ad1986a_modes;
1116 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001117 spec->need_dac_fix = 1;
1118 spec->multiout.max_channels = 2;
1119 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001120 break;
1121 case AD1986A_LAPTOP:
1122 spec->mixers[0] = ad1986a_laptop_mixers;
1123 spec->multiout.max_channels = 2;
1124 spec->multiout.num_dacs = 1;
1125 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1126 break;
Takashi Iwai825aa972006-03-17 10:50:49 +01001127 case AD1986A_LAPTOP_EAPD:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001128 spec->num_mixers = 3;
1129 spec->mixers[0] = ad1986a_laptop_master_mixers;
1130 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1131 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001132 spec->num_init_verbs = 2;
1133 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1134 spec->multiout.max_channels = 2;
1135 spec->multiout.num_dacs = 1;
1136 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1137 if (!is_jack_available(codec, 0x25))
1138 spec->multiout.dig_out_nid = 0;
1139 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1140 break;
1141 case AD1986A_SAMSUNG:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001142 spec->num_mixers = 2;
1143 spec->mixers[0] = ad1986a_laptop_master_mixers;
1144 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001145 spec->num_init_verbs = 3;
Takashi Iwai825aa972006-03-17 10:50:49 +01001146 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001147 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa972006-03-17 10:50:49 +01001148 spec->multiout.max_channels = 2;
1149 spec->multiout.num_dacs = 1;
1150 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001151 if (!is_jack_available(codec, 0x25))
1152 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001153 spec->input_mux = &ad1986a_automic_capture_source;
1154 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1155 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa972006-03-17 10:50:49 +01001156 break;
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001157 case AD1986A_SAMSUNG_P50:
1158 spec->num_mixers = 2;
1159 spec->mixers[0] = ad1986a_automute_master_mixers;
1160 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1161 spec->num_init_verbs = 4;
1162 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1163 spec->init_verbs[2] = ad1986a_automic_verbs;
1164 spec->init_verbs[3] = ad1986a_hp_init_verbs;
1165 spec->multiout.max_channels = 2;
1166 spec->multiout.num_dacs = 1;
1167 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1168 if (!is_jack_available(codec, 0x25))
1169 spec->multiout.dig_out_nid = 0;
1170 spec->input_mux = &ad1986a_automic_capture_source;
1171 codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
1172 codec->patch_ops.init = ad1986a_samsung_p50_init;
1173 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001174 case AD1986A_LAPTOP_AUTOMUTE:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001175 spec->num_mixers = 3;
1176 spec->mixers[0] = ad1986a_automute_master_mixers;
1177 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1178 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001179 spec->num_init_verbs = 3;
1180 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1181 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1182 spec->multiout.max_channels = 2;
1183 spec->multiout.num_dacs = 1;
1184 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001185 if (!is_jack_available(codec, 0x25))
1186 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001187 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1188 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1189 codec->patch_ops.init = ad1986a_hp_init;
Takashi Iwai03c405a2009-06-24 14:10:15 +02001190 /* Lenovo N100 seems to report the reversed bit
1191 * for HP jack-sensing
1192 */
1193 spec->inv_jack_detect = 1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001194 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001195 case AD1986A_ULTRA:
1196 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1197 spec->num_init_verbs = 2;
1198 spec->init_verbs[1] = ad1986a_ultra_init;
1199 spec->multiout.max_channels = 2;
1200 spec->multiout.num_dacs = 1;
1201 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1202 spec->multiout.dig_out_nid = 0;
1203 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001204 }
1205
Takashi Iwaid29240c2007-10-26 12:35:56 +02001206 /* AD1986A has a hardware problem that it can't share a stream
1207 * with multiple output pins. The copy of front to surrounds
1208 * causes noisy or silent outputs at a certain timing, e.g.
1209 * changing the volume.
1210 * So, let's disable the shared stream.
1211 */
1212 spec->multiout.no_share_stream = 1;
1213
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 return 0;
1215}
1216
1217/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001218 * AD1983 specific
1219 */
1220
1221#define AD1983_SPDIF_OUT 0x02
1222#define AD1983_DAC 0x03
1223#define AD1983_ADC 0x04
1224
1225static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001226static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001227static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001228
1229static struct hda_input_mux ad1983_capture_source = {
1230 .num_items = 4,
1231 .items = {
1232 { "Mic", 0x0 },
1233 { "Line", 0x1 },
1234 { "Mix", 0x2 },
1235 { "Mix Mono", 0x3 },
1236 },
1237};
1238
1239/*
1240 * SPDIF playback route
1241 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001242static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001243{
1244 static char *texts[] = { "PCM", "ADC" };
1245
1246 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1247 uinfo->count = 1;
1248 uinfo->value.enumerated.items = 2;
1249 if (uinfo->value.enumerated.item > 1)
1250 uinfo->value.enumerated.item = 1;
1251 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1252 return 0;
1253}
1254
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001255static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001256{
1257 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1258 struct ad198x_spec *spec = codec->spec;
1259
1260 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1261 return 0;
1262}
1263
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001264static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001265{
1266 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1267 struct ad198x_spec *spec = codec->spec;
1268
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001269 if (ucontrol->value.enumerated.item[0] > 1)
1270 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001271 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1272 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001273 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1274 AC_VERB_SET_CONNECT_SEL,
1275 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001276 return 1;
1277 }
1278 return 0;
1279}
1280
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001281static struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001282 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1283 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1284 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1285 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1286 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1287 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1288 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1289 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1290 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1291 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1292 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1293 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001294 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
1295 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1296 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1297 {
1298 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1299 .name = "Capture Source",
1300 .info = ad198x_mux_enum_info,
1301 .get = ad198x_mux_enum_get,
1302 .put = ad198x_mux_enum_put,
1303 },
1304 {
1305 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001306 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001307 .info = ad1983_spdif_route_info,
1308 .get = ad1983_spdif_route_get,
1309 .put = ad1983_spdif_route_put,
1310 },
1311 { } /* end */
1312};
1313
1314static struct hda_verb ad1983_init_verbs[] = {
1315 /* Front, HP, Mono; mute as default */
1316 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1317 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1318 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1319 /* Beep, PCM, Mic, Line-In: mute */
1320 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1321 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1322 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1323 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1324 /* Front, HP selectors; from Mix */
1325 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1326 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1327 /* Mono selector; from Mix */
1328 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1329 /* Mic selector; Mic */
1330 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1331 /* Line-in selector: Line-in */
1332 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1333 /* Mic boost: 0dB */
1334 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1335 /* Record selector: mic */
1336 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1337 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1338 /* SPDIF route: PCM */
1339 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1340 /* Front Pin */
1341 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1342 /* HP Pin */
1343 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1344 /* Mono Pin */
1345 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1346 /* Mic Pin */
1347 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1348 /* Line Pin */
1349 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1350 { } /* end */
1351};
1352
Takashi Iwaicb53c622007-08-10 17:21:45 +02001353#ifdef CONFIG_SND_HDA_POWER_SAVE
1354static struct hda_amp_list ad1983_loopbacks[] = {
1355 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1356 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1357 { } /* end */
1358};
1359#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001360
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001361static int patch_ad1983(struct hda_codec *codec)
1362{
1363 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001364 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001365
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001366 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001367 if (spec == NULL)
1368 return -ENOMEM;
1369
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001370 codec->spec = spec;
1371
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001372 err = snd_hda_attach_beep_device(codec, 0x10);
1373 if (err < 0) {
1374 ad198x_free(codec);
1375 return err;
1376 }
1377 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1378
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001379 spec->multiout.max_channels = 2;
1380 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1381 spec->multiout.dac_nids = ad1983_dac_nids;
1382 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001383 spec->num_adc_nids = 1;
1384 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001385 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001386 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001387 spec->num_mixers = 1;
1388 spec->mixers[0] = ad1983_mixers;
1389 spec->num_init_verbs = 1;
1390 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001391 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001392#ifdef CONFIG_SND_HDA_POWER_SAVE
1393 spec->loopback.amplist = ad1983_loopbacks;
1394#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001395 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001396
1397 codec->patch_ops = ad198x_patch_ops;
1398
1399 return 0;
1400}
1401
1402
1403/*
1404 * AD1981 HD specific
1405 */
1406
1407#define AD1981_SPDIF_OUT 0x02
1408#define AD1981_DAC 0x03
1409#define AD1981_ADC 0x04
1410
1411static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001412static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001413static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001414
1415/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
1416static struct hda_input_mux ad1981_capture_source = {
1417 .num_items = 7,
1418 .items = {
1419 { "Front Mic", 0x0 },
1420 { "Line", 0x1 },
1421 { "Mix", 0x2 },
1422 { "Mix Mono", 0x3 },
1423 { "CD", 0x4 },
1424 { "Mic", 0x6 },
1425 { "Aux", 0x7 },
1426 },
1427};
1428
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001429static struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001430 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1431 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1432 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1433 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1434 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1435 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1436 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1437 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1438 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1439 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1440 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1441 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1442 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1443 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1444 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1445 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1446 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1447 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001448 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
1449 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
1450 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1451 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1452 {
1453 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1454 .name = "Capture Source",
1455 .info = ad198x_mux_enum_info,
1456 .get = ad198x_mux_enum_get,
1457 .put = ad198x_mux_enum_put,
1458 },
1459 /* identical with AD1983 */
1460 {
1461 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001462 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001463 .info = ad1983_spdif_route_info,
1464 .get = ad1983_spdif_route_get,
1465 .put = ad1983_spdif_route_put,
1466 },
1467 { } /* end */
1468};
1469
1470static struct hda_verb ad1981_init_verbs[] = {
1471 /* Front, HP, Mono; mute as default */
1472 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1473 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1474 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1475 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1476 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1477 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1478 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1479 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1480 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1481 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1482 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1483 /* Front, HP selectors; from Mix */
1484 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1485 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1486 /* Mono selector; from Mix */
1487 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1488 /* Mic Mixer; select Front Mic */
1489 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1490 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1491 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001492 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1493 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001494 /* Record selector: Front mic */
1495 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1496 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1497 /* SPDIF route: PCM */
1498 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1499 /* Front Pin */
1500 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1501 /* HP Pin */
1502 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1503 /* Mono Pin */
1504 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1505 /* Front & Rear Mic Pins */
1506 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1507 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1508 /* Line Pin */
1509 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1510 /* Digital Beep */
1511 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1512 /* Line-Out as Input: disabled */
1513 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1514 { } /* end */
1515};
1516
Takashi Iwaicb53c622007-08-10 17:21:45 +02001517#ifdef CONFIG_SND_HDA_POWER_SAVE
1518static struct hda_amp_list ad1981_loopbacks[] = {
1519 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1520 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1521 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1522 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1523 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1524 { } /* end */
1525};
1526#endif
1527
Takashi Iwai18a815d2006-03-01 19:54:39 +01001528/*
1529 * Patch for HP nx6320
1530 *
Tobin Davis18768992007-03-12 22:20:51 +01001531 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001532 * speaker output enabled _and_ mute-LED off.
1533 */
1534
1535#define AD1981_HP_EVENT 0x37
1536#define AD1981_MIC_EVENT 0x38
1537
1538static struct hda_verb ad1981_hp_init_verbs[] = {
1539 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1540 /* pin sensing on HP and Mic jacks */
1541 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1542 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1543 {}
1544};
1545
1546/* turn on/off EAPD (+ mute HP) as a master switch */
1547static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1548 struct snd_ctl_elem_value *ucontrol)
1549{
1550 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1551 struct ad198x_spec *spec = codec->spec;
1552
1553 if (! ad198x_eapd_put(kcontrol, ucontrol))
1554 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001555 /* change speaker pin appropriately */
1556 snd_hda_codec_write(codec, 0x05, 0,
1557 AC_VERB_SET_PIN_WIDGET_CONTROL,
1558 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001559 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001560 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1561 HDA_AMP_MUTE,
1562 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001563 return 1;
1564}
1565
1566/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02001567static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
1568 .ops = &snd_hda_bind_vol,
1569 .values = {
1570 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1571 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1572 0
1573 },
1574};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001575
1576/* mute internal speaker if HP is plugged */
1577static void ad1981_hp_automute(struct hda_codec *codec)
1578{
1579 unsigned int present;
1580
Takashi Iwaid56757a2009-11-18 08:00:14 +01001581 present = snd_hda_jack_detect(codec, 0x06);
Takashi Iwai47fd8302007-08-10 17:11:07 +02001582 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1583 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001584}
1585
1586/* toggle input of built-in and mic jack appropriately */
1587static void ad1981_hp_automic(struct hda_codec *codec)
1588{
1589 static struct hda_verb mic_jack_on[] = {
1590 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1591 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1592 {}
1593 };
1594 static struct hda_verb mic_jack_off[] = {
1595 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1596 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1597 {}
1598 };
1599 unsigned int present;
1600
Takashi Iwaid56757a2009-11-18 08:00:14 +01001601 present = snd_hda_jack_detect(codec, 0x08);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001602 if (present)
1603 snd_hda_sequence_write(codec, mic_jack_on);
1604 else
1605 snd_hda_sequence_write(codec, mic_jack_off);
1606}
1607
1608/* unsolicited event for HP jack sensing */
1609static void ad1981_hp_unsol_event(struct hda_codec *codec,
1610 unsigned int res)
1611{
1612 res >>= 26;
1613 switch (res) {
1614 case AD1981_HP_EVENT:
1615 ad1981_hp_automute(codec);
1616 break;
1617 case AD1981_MIC_EVENT:
1618 ad1981_hp_automic(codec);
1619 break;
1620 }
1621}
1622
1623static struct hda_input_mux ad1981_hp_capture_source = {
1624 .num_items = 3,
1625 .items = {
1626 { "Mic", 0x0 },
1627 { "Docking-Station", 0x1 },
1628 { "Mix", 0x2 },
1629 },
1630};
1631
1632static struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001633 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001634 {
1635 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001636 .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
Takashi Iwai18a815d2006-03-01 19:54:39 +01001637 .name = "Master Playback Switch",
1638 .info = ad198x_eapd_info,
1639 .get = ad198x_eapd_get,
1640 .put = ad1981_hp_master_sw_put,
1641 .private_value = 0x05,
1642 },
1643 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1644 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1645#if 0
1646 /* FIXME: analog mic/line loopback doesn't work with my tests...
1647 * (although recording is OK)
1648 */
1649 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1650 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1651 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1652 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1653 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1654 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1655 /* FIXME: does this laptop have analog CD connection? */
1656 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1657 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1658#endif
1659 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1660 HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
1661 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1662 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1663 {
1664 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1665 .name = "Capture Source",
1666 .info = ad198x_mux_enum_info,
1667 .get = ad198x_mux_enum_get,
1668 .put = ad198x_mux_enum_put,
1669 },
1670 { } /* end */
1671};
1672
1673/* initialize jack-sensing, too */
1674static int ad1981_hp_init(struct hda_codec *codec)
1675{
1676 ad198x_init(codec);
1677 ad1981_hp_automute(codec);
1678 ad1981_hp_automic(codec);
1679 return 0;
1680}
1681
Tobin Davis18768992007-03-12 22:20:51 +01001682/* configuration for Toshiba Laptops */
1683static struct hda_verb ad1981_toshiba_init_verbs[] = {
1684 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1685 /* pin sensing on HP and Mic jacks */
1686 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1687 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1688 {}
1689};
1690
1691static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
1692 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1693 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1694 { }
1695};
1696
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001697/* configuration for Lenovo Thinkpad T60 */
1698static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
1699 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1700 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1701 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1702 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1703 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1704 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1705 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1706 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1707 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1708 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1709 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1710 {
1711 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1712 .name = "Capture Source",
1713 .info = ad198x_mux_enum_info,
1714 .get = ad198x_mux_enum_get,
1715 .put = ad198x_mux_enum_put,
1716 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001717 /* identical with AD1983 */
1718 {
1719 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1720 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1721 .info = ad1983_spdif_route_info,
1722 .get = ad1983_spdif_route_get,
1723 .put = ad1983_spdif_route_put,
1724 },
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001725 { } /* end */
1726};
1727
1728static struct hda_input_mux ad1981_thinkpad_capture_source = {
1729 .num_items = 3,
1730 .items = {
1731 { "Mic", 0x0 },
1732 { "Mix", 0x2 },
1733 { "CD", 0x4 },
1734 },
1735};
1736
Takashi Iwai18a815d2006-03-01 19:54:39 +01001737/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001738enum {
1739 AD1981_BASIC,
1740 AD1981_HP,
1741 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001742 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001743 AD1981_MODELS
1744};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001745
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001746static const char *ad1981_models[AD1981_MODELS] = {
1747 [AD1981_HP] = "hp",
1748 [AD1981_THINKPAD] = "thinkpad",
1749 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001750 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001751};
1752
1753static struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001754 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001755 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001756 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001757 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001758 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001759 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001760 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001761 /* HP nx6320 (reversed SSID, H/W bug) */
1762 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001763 {}
1764};
1765
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001766static int patch_ad1981(struct hda_codec *codec)
1767{
1768 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001769 int err, board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001770
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001771 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001772 if (spec == NULL)
1773 return -ENOMEM;
1774
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001775 codec->spec = spec;
1776
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001777 err = snd_hda_attach_beep_device(codec, 0x10);
1778 if (err < 0) {
1779 ad198x_free(codec);
1780 return err;
1781 }
1782 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
1783
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001784 spec->multiout.max_channels = 2;
1785 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1786 spec->multiout.dac_nids = ad1981_dac_nids;
1787 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001788 spec->num_adc_nids = 1;
1789 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001790 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001791 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001792 spec->num_mixers = 1;
1793 spec->mixers[0] = ad1981_mixers;
1794 spec->num_init_verbs = 1;
1795 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001796 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001797#ifdef CONFIG_SND_HDA_POWER_SAVE
1798 spec->loopback.amplist = ad1981_loopbacks;
1799#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001800 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001801
1802 codec->patch_ops = ad198x_patch_ops;
1803
Takashi Iwai18a815d2006-03-01 19:54:39 +01001804 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001805 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1806 ad1981_models,
1807 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001808 switch (board_config) {
1809 case AD1981_HP:
1810 spec->mixers[0] = ad1981_hp_mixers;
1811 spec->num_init_verbs = 2;
1812 spec->init_verbs[1] = ad1981_hp_init_verbs;
1813 spec->multiout.dig_out_nid = 0;
1814 spec->input_mux = &ad1981_hp_capture_source;
1815
1816 codec->patch_ops.init = ad1981_hp_init;
1817 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1818 break;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001819 case AD1981_THINKPAD:
1820 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001821 spec->input_mux = &ad1981_thinkpad_capture_source;
1822 break;
Tobin Davis18768992007-03-12 22:20:51 +01001823 case AD1981_TOSHIBA:
1824 spec->mixers[0] = ad1981_hp_mixers;
1825 spec->mixers[1] = ad1981_toshiba_mixers;
1826 spec->num_init_verbs = 2;
1827 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1828 spec->multiout.dig_out_nid = 0;
1829 spec->input_mux = &ad1981_hp_capture_source;
1830 codec->patch_ops.init = ad1981_hp_init;
1831 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1832 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001833 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001834 return 0;
1835}
1836
1837
1838/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001839 * AD1988
1840 *
1841 * Output pins and routes
1842 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001843 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001844 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1845 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1846 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1847 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1848 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1849 * port-F 0x16 (mute) <- 0x2a <- 06
1850 * port-G 0x24 (mute) <- 0x27 <- 05
1851 * port-H 0x25 (mute) <- 0x28 <- 0a
1852 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1853 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001854 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1855 * (*) 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 +01001856 *
1857 * Input pins and routes
1858 *
1859 * pin boost mix input # / adc input #
1860 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1861 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1862 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1863 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1864 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1865 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1866 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1867 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1868 *
1869 *
1870 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001871 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001872 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001873 *
1874 * Inputs of Analog Mix (0x20)
1875 * 0:Port-B (front mic)
1876 * 1:Port-C/G/H (line-in)
1877 * 2:Port-A
1878 * 3:Port-D (line-in/2)
1879 * 4:Port-E/G/H (mic-in)
1880 * 5:Port-F (mic2-in)
1881 * 6:CD
1882 * 7:Beep
1883 *
1884 * ADC selection
1885 * 0:Port-A
1886 * 1:Port-B (front mic-in)
1887 * 2:Port-C (line-in)
1888 * 3:Port-F (mic2-in)
1889 * 4:Port-E (mic-in)
1890 * 5:CD
1891 * 6:Port-G
1892 * 7:Port-H
1893 * 8:Port-D (line-in/2)
1894 * 9:Mix
1895 *
1896 * Proposed pin assignments by the datasheet
1897 *
1898 * 6-stack
1899 * Port-A front headphone
1900 * B front mic-in
1901 * C rear line-in
1902 * D rear front-out
1903 * E rear mic-in
1904 * F rear surround
1905 * G rear CLFE
1906 * H rear side
1907 *
1908 * 3-stack
1909 * Port-A front headphone
1910 * B front mic
1911 * C rear line-in/surround
1912 * D rear front-out
1913 * E rear mic-in/CLFE
1914 *
1915 * laptop
1916 * Port-A headphone
1917 * B mic-in
1918 * C docking station
1919 * D internal speaker (with EAPD)
1920 * E/F quad mic array
1921 */
1922
1923
1924/* models */
1925enum {
1926 AD1988_6STACK,
1927 AD1988_6STACK_DIG,
1928 AD1988_3STACK,
1929 AD1988_3STACK_DIG,
1930 AD1988_LAPTOP,
1931 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01001932 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001933 AD1988_MODEL_LAST,
1934};
1935
Takashi Iwaid32410b12005-11-24 16:06:23 +01001936/* reivision id to check workarounds */
1937#define AD1988A_REV2 0x100200
1938
Takashi Iwai1a806f42006-07-03 15:58:16 +02001939#define is_rev2(codec) \
1940 ((codec)->vendor_id == 0x11d41988 && \
1941 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001942
1943/*
1944 * mixers
1945 */
1946
Takashi Iwaid32410b12005-11-24 16:06:23 +01001947static hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001948 0x04, 0x06, 0x05, 0x0a
1949};
1950
Takashi Iwaid32410b12005-11-24 16:06:23 +01001951static hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001952 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01001953};
1954
1955/* for AD1988A revision-2, DAC2-4 are swapped */
1956static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
1957 0x04, 0x05, 0x0a, 0x06
1958};
1959
1960static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001961 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01001962};
1963
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001964static hda_nid_t ad1988_adc_nids[3] = {
1965 0x08, 0x09, 0x0f
1966};
1967
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001968static hda_nid_t ad1988_capsrc_nids[3] = {
1969 0x0c, 0x0d, 0x0e
1970};
1971
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001972#define AD1988_SPDIF_OUT 0x02
1973#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001974#define AD1988_SPDIF_IN 0x07
1975
Takashi Iwai3a08e302009-02-13 11:37:08 +01001976static hda_nid_t ad1989b_slave_dig_outs[] = {
1977 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001978};
1979
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001980static struct hda_input_mux ad1988_6stack_capture_source = {
1981 .num_items = 5,
1982 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001983 { "Front Mic", 0x1 }, /* port-B */
1984 { "Line", 0x2 }, /* port-C */
1985 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001986 { "CD", 0x5 },
1987 { "Mix", 0x9 },
1988 },
1989};
1990
1991static struct hda_input_mux ad1988_laptop_capture_source = {
1992 .num_items = 3,
1993 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001994 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001995 { "CD", 0x5 },
1996 { "Mix", 0x9 },
1997 },
1998};
1999
2000/*
2001 */
2002static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
2003 struct snd_ctl_elem_info *uinfo)
2004{
2005 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2006 struct ad198x_spec *spec = codec->spec;
2007 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
2008 spec->num_channel_mode);
2009}
2010
2011static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
2012 struct snd_ctl_elem_value *ucontrol)
2013{
2014 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2015 struct ad198x_spec *spec = codec->spec;
2016 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
2017 spec->num_channel_mode, spec->multiout.max_channels);
2018}
2019
2020static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
2021 struct snd_ctl_elem_value *ucontrol)
2022{
2023 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2024 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002025 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
2026 spec->num_channel_mode,
2027 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02002028 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02002029 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002030 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002031}
2032
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002033/* 6-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002034static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002035 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2036 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
2037 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2038 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
2039 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002040 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002041};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002042
Takashi Iwaid32410b12005-11-24 16:06:23 +01002043static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
2044 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2045 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2046 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2047 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2048 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002049 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002050};
2051
2052static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002053 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2054 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2055 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2056 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2057 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2058 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2059 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2060
2061 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2062 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2063 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2064 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2065 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2066 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2067 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2068 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2069
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002070 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002071 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2072
2073 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2074 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2075
2076 { } /* end */
2077};
2078
2079/* 3-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002080static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002081 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002082 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002083 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2084 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002085 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002086};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002087
Takashi Iwaid32410b12005-11-24 16:06:23 +01002088static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
2089 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002090 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2091 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2092 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002093 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002094};
2095
2096static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002097 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002098 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2099 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2100 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002101 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2102 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2103
2104 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2105 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2106 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2107 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2108 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2109 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2110 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2111 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2112
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002113 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002114 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2115
2116 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2117 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2118 {
2119 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2120 .name = "Channel Mode",
2121 .info = ad198x_ch_mode_info,
2122 .get = ad198x_ch_mode_get,
2123 .put = ad198x_ch_mode_put,
2124 },
2125
2126 { } /* end */
2127};
2128
2129/* laptop mode */
2130static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
2131 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2132 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2133 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2134
2135 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2136 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2137 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2138 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2139 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2140 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2141
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002142 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002143 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2144
2145 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2146
2147 {
2148 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2149 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002150 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01002151 .info = ad198x_eapd_info,
2152 .get = ad198x_eapd_get,
2153 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01002154 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002155 },
2156
2157 { } /* end */
2158};
2159
2160/* capture */
2161static struct snd_kcontrol_new ad1988_capture_mixers[] = {
2162 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2163 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2164 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2165 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2166 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2167 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2168 {
2169 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2170 /* The multiple "Capture Source" controls confuse alsamixer
2171 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002172 */
2173 /* .name = "Capture Source", */
2174 .name = "Input Source",
2175 .count = 3,
2176 .info = ad198x_mux_enum_info,
2177 .get = ad198x_mux_enum_get,
2178 .put = ad198x_mux_enum_put,
2179 },
2180 { } /* end */
2181};
2182
2183static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2184 struct snd_ctl_elem_info *uinfo)
2185{
2186 static char *texts[] = {
2187 "PCM", "ADC1", "ADC2", "ADC3"
2188 };
2189 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2190 uinfo->count = 1;
2191 uinfo->value.enumerated.items = 4;
2192 if (uinfo->value.enumerated.item >= 4)
2193 uinfo->value.enumerated.item = 3;
2194 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2195 return 0;
2196}
2197
2198static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2199 struct snd_ctl_elem_value *ucontrol)
2200{
2201 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2202 unsigned int sel;
2203
Takashi Iwaibddcf542007-07-24 18:04:05 +02002204 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2205 AC_AMP_GET_INPUT);
2206 if (!(sel & 0x80))
2207 ucontrol->value.enumerated.item[0] = 0;
2208 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002209 sel = snd_hda_codec_read(codec, 0x0b, 0,
2210 AC_VERB_GET_CONNECT_SEL, 0);
2211 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002212 sel++;
2213 else
2214 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002215 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002216 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002217 return 0;
2218}
2219
2220static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2221 struct snd_ctl_elem_value *ucontrol)
2222{
2223 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002224 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002225 int change;
2226
Takashi Iwai35b26722007-05-05 12:17:17 +02002227 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002228 if (val > 3)
2229 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002230 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002231 sel = snd_hda_codec_read(codec, 0x1d, 0,
2232 AC_VERB_GET_AMP_GAIN_MUTE,
2233 AC_AMP_GET_INPUT);
2234 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002235 if (change) {
2236 snd_hda_codec_write_cache(codec, 0x1d, 0,
2237 AC_VERB_SET_AMP_GAIN_MUTE,
2238 AMP_IN_UNMUTE(0));
2239 snd_hda_codec_write_cache(codec, 0x1d, 0,
2240 AC_VERB_SET_AMP_GAIN_MUTE,
2241 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002242 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002243 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002244 sel = snd_hda_codec_read(codec, 0x1d, 0,
2245 AC_VERB_GET_AMP_GAIN_MUTE,
2246 AC_AMP_GET_INPUT | 0x01);
2247 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002248 if (change) {
2249 snd_hda_codec_write_cache(codec, 0x1d, 0,
2250 AC_VERB_SET_AMP_GAIN_MUTE,
2251 AMP_IN_MUTE(0));
2252 snd_hda_codec_write_cache(codec, 0x1d, 0,
2253 AC_VERB_SET_AMP_GAIN_MUTE,
2254 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002255 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002256 sel = snd_hda_codec_read(codec, 0x0b, 0,
2257 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2258 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002259 if (change)
2260 snd_hda_codec_write_cache(codec, 0x0b, 0,
2261 AC_VERB_SET_CONNECT_SEL,
2262 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002263 }
2264 return change;
2265}
2266
2267static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
2268 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2269 {
2270 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2271 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002272 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002273 .info = ad1988_spdif_playback_source_info,
2274 .get = ad1988_spdif_playback_source_get,
2275 .put = ad1988_spdif_playback_source_put,
2276 },
2277 { } /* end */
2278};
2279
2280static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
2281 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2282 { } /* end */
2283};
2284
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002285static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
2286 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002287 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002288 { } /* end */
2289};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002290
2291/*
2292 * initialization verbs
2293 */
2294
2295/*
2296 * for 6-stack (+dig)
2297 */
2298static struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002299 /* Front, Surround, CLFE, side DAC; unmute as default */
2300 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2301 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2302 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2303 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002304 /* Port-A front headphon path */
2305 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2306 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2307 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2308 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2309 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2310 /* Port-D line-out path */
2311 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2312 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2313 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2314 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2315 /* Port-F surround path */
2316 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2317 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2318 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2319 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2320 /* Port-G CLFE path */
2321 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2322 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2323 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2324 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2325 /* Port-H side path */
2326 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2327 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2328 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2329 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2330 /* Mono out path */
2331 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2332 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2333 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2334 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2335 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2336 /* Port-B front mic-in path */
2337 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2338 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2339 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2340 /* Port-C line-in path */
2341 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2342 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2343 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2344 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2345 /* Port-E mic-in path */
2346 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2347 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2348 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2349 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002350 /* Analog CD Input */
2351 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002352 /* Analog Mix output amp */
2353 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002354
2355 { }
2356};
2357
2358static struct hda_verb ad1988_capture_init_verbs[] = {
2359 /* mute analog mix */
2360 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2361 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2362 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2363 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2364 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2365 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2366 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2367 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2368 /* select ADCs - front-mic */
2369 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2370 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2371 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002372
2373 { }
2374};
2375
2376static struct hda_verb ad1988_spdif_init_verbs[] = {
2377 /* SPDIF out sel */
2378 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2379 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2380 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002381 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002382 /* SPDIF out pin */
2383 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002384
2385 { }
2386};
2387
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002388/* AD1989 has no ADC -> SPDIF route */
2389static struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002390 /* SPDIF-1 out pin */
2391 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002392 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002393 /* SPDIF-2/HDMI out pin */
2394 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2395 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002396 { }
2397};
2398
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002399/*
2400 * verbs for 3stack (+dig)
2401 */
2402static struct hda_verb ad1988_3stack_ch2_init[] = {
2403 /* set port-C to line-in */
2404 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2405 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2406 /* set port-E to mic-in */
2407 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2408 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2409 { } /* end */
2410};
2411
2412static struct hda_verb ad1988_3stack_ch6_init[] = {
2413 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002414 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002415 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002416 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002417 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002418 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002419 { } /* end */
2420};
2421
2422static struct hda_channel_mode ad1988_3stack_modes[2] = {
2423 { 2, ad1988_3stack_ch2_init },
2424 { 6, ad1988_3stack_ch6_init },
2425};
2426
2427static struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002428 /* Front, Surround, CLFE, side DAC; unmute as default */
2429 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2430 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2431 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2432 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002433 /* Port-A front headphon path */
2434 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2435 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2436 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2437 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2438 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2439 /* Port-D line-out path */
2440 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2441 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2442 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2443 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2444 /* Mono out path */
2445 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2446 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2447 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2448 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2449 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2450 /* Port-B front mic-in path */
2451 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2452 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2453 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002454 /* Port-C line-in/surround path - 6ch mode as default */
2455 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2456 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002457 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002458 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002459 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002460 /* Port-E mic-in/CLFE path - 6ch mode as default */
2461 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2462 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002463 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002464 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002465 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2466 /* mute analog mix */
2467 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2468 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2469 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2470 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2471 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2472 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2473 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2474 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2475 /* select ADCs - front-mic */
2476 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2477 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2478 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002479 /* Analog Mix output amp */
2480 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002481 { }
2482};
2483
2484/*
2485 * verbs for laptop mode (+dig)
2486 */
2487static struct hda_verb ad1988_laptop_hp_on[] = {
2488 /* unmute port-A and mute port-D */
2489 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2490 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2491 { } /* end */
2492};
2493static struct hda_verb ad1988_laptop_hp_off[] = {
2494 /* mute port-A and unmute port-D */
2495 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2496 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2497 { } /* end */
2498};
2499
2500#define AD1988_HP_EVENT 0x01
2501
2502static struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002503 /* Front, Surround, CLFE, side DAC; unmute as default */
2504 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2505 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2506 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2507 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002508 /* Port-A front headphon path */
2509 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2510 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2511 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2512 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2513 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2514 /* unsolicited event for pin-sense */
2515 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2516 /* Port-D line-out path + EAPD */
2517 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2518 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2519 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2520 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2521 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2522 /* Mono out path */
2523 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2524 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2525 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2526 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2527 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2528 /* Port-B mic-in path */
2529 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2530 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2531 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2532 /* Port-C docking station - try to output */
2533 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2534 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2535 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2536 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2537 /* mute analog mix */
2538 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2539 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2540 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2541 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2542 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2543 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2544 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2545 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2546 /* select ADCs - mic */
2547 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2548 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2549 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002550 /* Analog Mix output amp */
2551 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002552 { }
2553};
2554
2555static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2556{
2557 if ((res >> 26) != AD1988_HP_EVENT)
2558 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01002559 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002560 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2561 else
2562 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2563}
2564
Takashi Iwaicb53c622007-08-10 17:21:45 +02002565#ifdef CONFIG_SND_HDA_POWER_SAVE
2566static struct hda_amp_list ad1988_loopbacks[] = {
2567 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2568 { 0x20, HDA_INPUT, 1 }, /* Line */
2569 { 0x20, HDA_INPUT, 4 }, /* Mic */
2570 { 0x20, HDA_INPUT, 6 }, /* CD */
2571 { } /* end */
2572};
2573#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002574
2575/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002576 * Automatic parse of I/O pins from the BIOS configuration
2577 */
2578
Takashi Iwaid32410b12005-11-24 16:06:23 +01002579enum {
2580 AD_CTL_WIDGET_VOL,
2581 AD_CTL_WIDGET_MUTE,
2582 AD_CTL_BIND_MUTE,
2583};
2584static struct snd_kcontrol_new ad1988_control_templates[] = {
2585 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2586 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2587 HDA_BIND_MUTE(NULL, 0, 0, 0),
2588};
2589
2590/* add dynamic controls */
2591static int add_control(struct ad198x_spec *spec, int type, const char *name,
2592 unsigned long val)
2593{
2594 struct snd_kcontrol_new *knew;
2595
Takashi Iwai603c4012008-07-30 15:01:44 +02002596 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2597 knew = snd_array_new(&spec->kctls);
2598 if (!knew)
2599 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002600 *knew = ad1988_control_templates[type];
2601 knew->name = kstrdup(name, GFP_KERNEL);
2602 if (! knew->name)
2603 return -ENOMEM;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +01002604 if (get_amp_nid_(val))
Jaroslav Kysela9e3fd872009-12-08 17:45:25 +01002605 knew->subdevice = HDA_SUBDEV_NID_FLAG |
2606 HDA_SUBDEV_AMP_FLAG |
2607 get_amp_nid_(val);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002608 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002609 return 0;
2610}
2611
2612#define AD1988_PIN_CD_NID 0x18
2613#define AD1988_PIN_BEEP_NID 0x10
2614
2615static hda_nid_t ad1988_mixer_nids[8] = {
2616 /* A B C D E F G H */
2617 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2618};
2619
2620static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2621{
2622 static hda_nid_t idx_to_dac[8] = {
2623 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002624 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002625 };
2626 static hda_nid_t idx_to_dac_rev2[8] = {
2627 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002628 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002629 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002630 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002631 return idx_to_dac_rev2[idx];
2632 else
2633 return idx_to_dac[idx];
2634}
2635
2636static hda_nid_t ad1988_boost_nids[8] = {
2637 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2638};
2639
2640static int ad1988_pin_idx(hda_nid_t nid)
2641{
2642 static hda_nid_t ad1988_io_pins[8] = {
2643 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2644 };
2645 int i;
2646 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2647 if (ad1988_io_pins[i] == nid)
2648 return i;
2649 return 0; /* should be -1 */
2650}
2651
2652static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2653{
2654 static int loopback_idx[8] = {
2655 2, 0, 1, 3, 4, 5, 1, 4
2656 };
2657 switch (nid) {
2658 case AD1988_PIN_CD_NID:
2659 return 6;
2660 default:
2661 return loopback_idx[ad1988_pin_idx(nid)];
2662 }
2663}
2664
2665static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2666{
2667 static int adc_idx[8] = {
2668 0, 1, 2, 8, 4, 3, 6, 7
2669 };
2670 switch (nid) {
2671 case AD1988_PIN_CD_NID:
2672 return 5;
2673 default:
2674 return adc_idx[ad1988_pin_idx(nid)];
2675 }
2676}
2677
2678/* fill in the dac_nids table from the parsed pin configuration */
2679static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2680 const struct auto_pin_cfg *cfg)
2681{
2682 struct ad198x_spec *spec = codec->spec;
2683 int i, idx;
2684
2685 spec->multiout.dac_nids = spec->private_dac_nids;
2686
2687 /* check the pins hardwired to audio widget */
2688 for (i = 0; i < cfg->line_outs; i++) {
2689 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2690 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2691 }
2692 spec->multiout.num_dacs = cfg->line_outs;
2693 return 0;
2694}
2695
2696/* add playback controls from the parsed DAC table */
2697static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2698 const struct auto_pin_cfg *cfg)
2699{
2700 char name[32];
2701 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2702 hda_nid_t nid;
2703 int i, err;
2704
2705 for (i = 0; i < cfg->line_outs; i++) {
2706 hda_nid_t dac = spec->multiout.dac_nids[i];
2707 if (! dac)
2708 continue;
2709 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2710 if (i == 2) {
2711 /* Center/LFE */
2712 err = add_control(spec, AD_CTL_WIDGET_VOL,
2713 "Center Playback Volume",
2714 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2715 if (err < 0)
2716 return err;
2717 err = add_control(spec, AD_CTL_WIDGET_VOL,
2718 "LFE Playback Volume",
2719 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2720 if (err < 0)
2721 return err;
2722 err = add_control(spec, AD_CTL_BIND_MUTE,
2723 "Center Playback Switch",
2724 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2725 if (err < 0)
2726 return err;
2727 err = add_control(spec, AD_CTL_BIND_MUTE,
2728 "LFE Playback Switch",
2729 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2730 if (err < 0)
2731 return err;
2732 } else {
2733 sprintf(name, "%s Playback Volume", chname[i]);
2734 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2735 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2736 if (err < 0)
2737 return err;
2738 sprintf(name, "%s Playback Switch", chname[i]);
2739 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2740 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2741 if (err < 0)
2742 return err;
2743 }
2744 }
2745 return 0;
2746}
2747
2748/* add playback controls for speaker and HP outputs */
2749static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2750 const char *pfx)
2751{
2752 struct ad198x_spec *spec = codec->spec;
2753 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02002754 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002755 char name[32];
2756
2757 if (! pin)
2758 return 0;
2759
2760 idx = ad1988_pin_idx(pin);
2761 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02002762 /* check whether the corresponding DAC was already taken */
2763 for (i = 0; i < spec->autocfg.line_outs; i++) {
2764 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2765 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
2766 if (dac == nid)
2767 break;
2768 }
2769 if (i >= spec->autocfg.line_outs) {
2770 /* specify the DAC as the extra output */
2771 if (!spec->multiout.hp_nid)
2772 spec->multiout.hp_nid = nid;
2773 else
2774 spec->multiout.extra_out_nid[0] = nid;
2775 /* control HP volume/switch on the output mixer amp */
2776 sprintf(name, "%s Playback Volume", pfx);
2777 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2778 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2779 if (err < 0)
2780 return err;
2781 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01002782 nid = ad1988_mixer_nids[idx];
2783 sprintf(name, "%s Playback Switch", pfx);
2784 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2785 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2786 return err;
2787 return 0;
2788}
2789
2790/* create input playback/capture controls for the given pin */
2791static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2792 const char *ctlname, int boost)
2793{
2794 char name[32];
2795 int err, idx;
2796
2797 sprintf(name, "%s Playback Volume", ctlname);
2798 idx = ad1988_pin_to_loopback_idx(pin);
2799 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2800 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2801 return err;
2802 sprintf(name, "%s Playback Switch", ctlname);
2803 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2804 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2805 return err;
2806 if (boost) {
2807 hda_nid_t bnid;
2808 idx = ad1988_pin_idx(pin);
2809 bnid = ad1988_boost_nids[idx];
2810 if (bnid) {
2811 sprintf(name, "%s Boost", ctlname);
2812 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2813 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2814
2815 }
2816 }
2817 return 0;
2818}
2819
2820/* create playback/capture controls for input pins */
2821static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2822 const struct auto_pin_cfg *cfg)
2823{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002824 struct hda_input_mux *imux = &spec->private_imux;
2825 int i, err;
2826
2827 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002828 err = new_analog_input(spec, cfg->input_pins[i],
2829 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002830 i <= AUTO_PIN_FRONT_MIC);
2831 if (err < 0)
2832 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002833 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002834 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2835 imux->num_items++;
2836 }
2837 imux->items[imux->num_items].label = "Mix";
2838 imux->items[imux->num_items].index = 9;
2839 imux->num_items++;
2840
2841 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2842 "Analog Mix Playback Volume",
2843 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2844 return err;
2845 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2846 "Analog Mix Playback Switch",
2847 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2848 return err;
2849
2850 return 0;
2851}
2852
2853static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2854 hda_nid_t nid, int pin_type,
2855 int dac_idx)
2856{
2857 /* set as output */
2858 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2859 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2860 switch (nid) {
2861 case 0x11: /* port-A - DAC 04 */
2862 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2863 break;
2864 case 0x14: /* port-B - DAC 06 */
2865 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2866 break;
2867 case 0x15: /* port-C - DAC 05 */
2868 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2869 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002870 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002871 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2872 break;
2873 case 0x13: /* mono - DAC 04 */
2874 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2875 break;
2876 }
2877}
2878
2879static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2880{
2881 struct ad198x_spec *spec = codec->spec;
2882 int i;
2883
2884 for (i = 0; i < spec->autocfg.line_outs; i++) {
2885 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2886 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2887 }
2888}
2889
2890static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2891{
2892 struct ad198x_spec *spec = codec->spec;
2893 hda_nid_t pin;
2894
Takashi Iwai82bc9552006-03-21 11:24:42 +01002895 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002896 if (pin) /* connect to front */
2897 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002898 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002899 if (pin) /* connect to front */
2900 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2901}
2902
2903static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2904{
2905 struct ad198x_spec *spec = codec->spec;
2906 int i, idx;
2907
2908 for (i = 0; i < AUTO_PIN_LAST; i++) {
2909 hda_nid_t nid = spec->autocfg.input_pins[i];
2910 if (! nid)
2911 continue;
2912 switch (nid) {
2913 case 0x15: /* port-C */
2914 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2915 break;
2916 case 0x17: /* port-E */
2917 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2918 break;
2919 }
2920 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2921 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2922 if (nid != AD1988_PIN_CD_NID)
2923 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
2924 AMP_OUT_MUTE);
2925 idx = ad1988_pin_idx(nid);
2926 if (ad1988_boost_nids[idx])
2927 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
2928 AC_VERB_SET_AMP_GAIN_MUTE,
2929 AMP_OUT_ZERO);
2930 }
2931}
2932
2933/* parse the BIOS configuration and set up the alc_spec */
2934/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
2935static int ad1988_parse_auto_config(struct hda_codec *codec)
2936{
2937 struct ad198x_spec *spec = codec->spec;
2938 int err;
2939
Kailang Yangdf694da2005-12-05 19:42:22 +01002940 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002941 return err;
2942 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
2943 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002944 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002945 return 0; /* can't find valid BIOS pin config */
2946 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01002947 (err = ad1988_auto_create_extra_out(codec,
2948 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002949 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002950 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002951 "Headphone")) < 0 ||
2952 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
2953 return err;
2954
2955 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2956
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002957 if (spec->autocfg.dig_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002958 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2959 if (spec->autocfg.dig_in_pin)
2960 spec->dig_in_nid = AD1988_SPDIF_IN;
2961
Takashi Iwai603c4012008-07-30 15:01:44 +02002962 if (spec->kctls.list)
2963 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002964
2965 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
2966
2967 spec->input_mux = &spec->private_imux;
2968
2969 return 1;
2970}
2971
2972/* init callback for auto-configuration model -- overriding the default init */
2973static int ad1988_auto_init(struct hda_codec *codec)
2974{
2975 ad198x_init(codec);
2976 ad1988_auto_init_multi_out(codec);
2977 ad1988_auto_init_extra_out(codec);
2978 ad1988_auto_init_analog_input(codec);
2979 return 0;
2980}
2981
2982
2983/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002984 */
2985
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002986static const char *ad1988_models[AD1988_MODEL_LAST] = {
2987 [AD1988_6STACK] = "6stack",
2988 [AD1988_6STACK_DIG] = "6stack-dig",
2989 [AD1988_3STACK] = "3stack",
2990 [AD1988_3STACK_DIG] = "3stack-dig",
2991 [AD1988_LAPTOP] = "laptop",
2992 [AD1988_LAPTOP_DIG] = "laptop-dig",
2993 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002994};
2995
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002996static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002997 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002998 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02002999 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07003000 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003001 {}
3002};
3003
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003004static int patch_ad1988(struct hda_codec *codec)
3005{
3006 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003007 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003008
3009 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3010 if (spec == NULL)
3011 return -ENOMEM;
3012
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003013 codec->spec = spec;
3014
Takashi Iwai1a806f42006-07-03 15:58:16 +02003015 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003016 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
3017
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003018 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003019 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003020 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02003021 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3022 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003023 board_config = AD1988_AUTO;
3024 }
3025
3026 if (board_config == AD1988_AUTO) {
3027 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003028 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003029 if (err < 0) {
3030 ad198x_free(codec);
3031 return err;
3032 } else if (! err) {
3033 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
3034 board_config = AD1988_6STACK;
3035 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003036 }
3037
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003038 err = snd_hda_attach_beep_device(codec, 0x10);
3039 if (err < 0) {
3040 ad198x_free(codec);
3041 return err;
3042 }
3043 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3044
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003045 switch (board_config) {
3046 case AD1988_6STACK:
3047 case AD1988_6STACK_DIG:
3048 spec->multiout.max_channels = 8;
3049 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003050 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003051 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3052 else
3053 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003054 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003055 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003056 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003057 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3058 else
3059 spec->mixers[0] = ad1988_6stack_mixers1;
3060 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003061 spec->num_init_verbs = 1;
3062 spec->init_verbs[0] = ad1988_6stack_init_verbs;
3063 if (board_config == AD1988_6STACK_DIG) {
3064 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3065 spec->dig_in_nid = AD1988_SPDIF_IN;
3066 }
3067 break;
3068 case AD1988_3STACK:
3069 case AD1988_3STACK_DIG:
3070 spec->multiout.max_channels = 6;
3071 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003072 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003073 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3074 else
3075 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003076 spec->input_mux = &ad1988_6stack_capture_source;
3077 spec->channel_mode = ad1988_3stack_modes;
3078 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003079 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003080 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003081 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3082 else
3083 spec->mixers[0] = ad1988_3stack_mixers1;
3084 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003085 spec->num_init_verbs = 1;
3086 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3087 if (board_config == AD1988_3STACK_DIG)
3088 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3089 break;
3090 case AD1988_LAPTOP:
3091 case AD1988_LAPTOP_DIG:
3092 spec->multiout.max_channels = 2;
3093 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003094 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003095 spec->input_mux = &ad1988_laptop_capture_source;
3096 spec->num_mixers = 1;
3097 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01003098 spec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003099 spec->num_init_verbs = 1;
3100 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3101 if (board_config == AD1988_LAPTOP_DIG)
3102 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3103 break;
3104 }
3105
Takashi Iwaid32410b12005-11-24 16:06:23 +01003106 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3107 spec->adc_nids = ad1988_adc_nids;
3108 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003109 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3110 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3111 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003112 if (codec->vendor_id >= 0x11d4989a) {
3113 spec->mixers[spec->num_mixers++] =
3114 ad1989_spdif_out_mixers;
3115 spec->init_verbs[spec->num_init_verbs++] =
3116 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003117 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003118 } else {
3119 spec->mixers[spec->num_mixers++] =
3120 ad1988_spdif_out_mixers;
3121 spec->init_verbs[spec->num_init_verbs++] =
3122 ad1988_spdif_init_verbs;
3123 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003124 }
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003125 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003126 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
3127
3128 codec->patch_ops = ad198x_patch_ops;
3129 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003130 case AD1988_AUTO:
3131 codec->patch_ops.init = ad1988_auto_init;
3132 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003133 case AD1988_LAPTOP:
3134 case AD1988_LAPTOP_DIG:
3135 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3136 break;
3137 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003138#ifdef CONFIG_SND_HDA_POWER_SAVE
3139 spec->loopback.amplist = ad1988_loopbacks;
3140#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003141 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003142
3143 return 0;
3144}
3145
3146
3147/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003148 * AD1884 / AD1984
3149 *
3150 * port-B - front line/mic-in
3151 * port-E - aux in/out
3152 * port-F - aux in/out
3153 * port-C - rear line/mic-in
3154 * port-D - rear line/hp-out
3155 * port-A - front line/hp-out
3156 *
3157 * AD1984 = AD1884 + two digital mic-ins
3158 *
3159 * FIXME:
3160 * For simplicity, we share the single DAC for both HP and line-outs
3161 * right now. The inidividual playbacks could be easily implemented,
3162 * but no build-up framework is given, so far.
3163 */
3164
3165static hda_nid_t ad1884_dac_nids[1] = {
3166 0x04,
3167};
3168
3169static hda_nid_t ad1884_adc_nids[2] = {
3170 0x08, 0x09,
3171};
3172
3173static hda_nid_t ad1884_capsrc_nids[2] = {
3174 0x0c, 0x0d,
3175};
3176
3177#define AD1884_SPDIF_OUT 0x02
3178
3179static struct hda_input_mux ad1884_capture_source = {
3180 .num_items = 4,
3181 .items = {
3182 { "Front Mic", 0x0 },
3183 { "Mic", 0x1 },
3184 { "CD", 0x2 },
3185 { "Mix", 0x3 },
3186 },
3187};
3188
3189static struct snd_kcontrol_new ad1884_base_mixers[] = {
3190 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3191 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3192 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3193 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3194 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3195 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3196 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3197 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3198 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3199 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3200 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3201 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003202 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
3203 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3204 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3205 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3206 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3207 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3208 {
3209 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3210 /* The multiple "Capture Source" controls confuse alsamixer
3211 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003212 */
3213 /* .name = "Capture Source", */
3214 .name = "Input Source",
3215 .count = 2,
3216 .info = ad198x_mux_enum_info,
3217 .get = ad198x_mux_enum_get,
3218 .put = ad198x_mux_enum_put,
3219 },
3220 /* SPDIF controls */
3221 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3222 {
3223 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3224 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3225 /* identical with ad1983 */
3226 .info = ad1983_spdif_route_info,
3227 .get = ad1983_spdif_route_get,
3228 .put = ad1983_spdif_route_put,
3229 },
3230 { } /* end */
3231};
3232
3233static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3234 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3235 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3236 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003237 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003238 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003239 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003240 { } /* end */
3241};
3242
3243/*
3244 * initialization verbs
3245 */
3246static struct hda_verb ad1884_init_verbs[] = {
3247 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003248 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3249 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003250 /* Port-A (HP) mixer */
3251 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3252 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3253 /* Port-A pin */
3254 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3255 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3256 /* HP selector - select DAC2 */
3257 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3258 /* Port-D (Line-out) mixer */
3259 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3260 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3261 /* Port-D pin */
3262 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3263 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3264 /* Mono-out mixer */
3265 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3266 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3267 /* Mono-out pin */
3268 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3269 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3270 /* Mono selector */
3271 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3272 /* Port-B (front mic) pin */
3273 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003274 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003275 /* Port-C (rear mic) pin */
3276 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003277 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003278 /* Analog mixer; mute as default */
3279 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3280 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3281 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3282 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3283 /* Analog Mix output amp */
3284 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3285 /* SPDIF output selector */
3286 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3287 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3288 { } /* end */
3289};
3290
Takashi Iwaicb53c622007-08-10 17:21:45 +02003291#ifdef CONFIG_SND_HDA_POWER_SAVE
3292static struct hda_amp_list ad1884_loopbacks[] = {
3293 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3294 { 0x20, HDA_INPUT, 1 }, /* Mic */
3295 { 0x20, HDA_INPUT, 2 }, /* CD */
3296 { 0x20, HDA_INPUT, 4 }, /* Docking */
3297 { } /* end */
3298};
3299#endif
3300
Takashi Iwai2134ea42008-01-10 16:53:55 +01003301static const char *ad1884_slave_vols[] = {
3302 "PCM Playback Volume",
3303 "Mic Playback Volume",
3304 "Mono Playback Volume",
3305 "Front Mic Playback Volume",
3306 "Mic Playback Volume",
3307 "CD Playback Volume",
3308 "Internal Mic Playback Volume",
Akinobu Mitabca68462009-04-06 18:42:42 +09003309 "Docking Mic Playback Volume",
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003310 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003311 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003312 NULL
3313};
3314
Takashi Iwai2bac6472007-05-18 18:21:41 +02003315static int patch_ad1884(struct hda_codec *codec)
3316{
3317 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003318 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003319
3320 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3321 if (spec == NULL)
3322 return -ENOMEM;
3323
Takashi Iwai2bac6472007-05-18 18:21:41 +02003324 codec->spec = spec;
3325
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003326 err = snd_hda_attach_beep_device(codec, 0x10);
3327 if (err < 0) {
3328 ad198x_free(codec);
3329 return err;
3330 }
3331 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3332
Takashi Iwai2bac6472007-05-18 18:21:41 +02003333 spec->multiout.max_channels = 2;
3334 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3335 spec->multiout.dac_nids = ad1884_dac_nids;
3336 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3337 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3338 spec->adc_nids = ad1884_adc_nids;
3339 spec->capsrc_nids = ad1884_capsrc_nids;
3340 spec->input_mux = &ad1884_capture_source;
3341 spec->num_mixers = 1;
3342 spec->mixers[0] = ad1884_base_mixers;
3343 spec->num_init_verbs = 1;
3344 spec->init_verbs[0] = ad1884_init_verbs;
3345 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003346#ifdef CONFIG_SND_HDA_POWER_SAVE
3347 spec->loopback.amplist = ad1884_loopbacks;
3348#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003349 spec->vmaster_nid = 0x04;
3350 /* we need to cover all playback volumes */
3351 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003352
3353 codec->patch_ops = ad198x_patch_ops;
3354
3355 return 0;
3356}
3357
3358/*
3359 * Lenovo Thinkpad T61/X61
3360 */
3361static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003362 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003363 .items = {
3364 { "Mic", 0x0 },
3365 { "Internal Mic", 0x1 },
3366 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003367 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003368 },
3369};
3370
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003371
3372/*
3373 * Dell Precision T3400
3374 */
3375static struct hda_input_mux ad1984_dell_desktop_capture_source = {
3376 .num_items = 3,
3377 .items = {
3378 { "Front Mic", 0x0 },
3379 { "Line-In", 0x1 },
3380 { "Mix", 0x3 },
3381 },
3382};
3383
3384
Takashi Iwai2bac6472007-05-18 18:21:41 +02003385static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3386 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3387 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3388 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3389 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3390 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3391 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3392 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3393 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3394 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3395 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003396 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3397 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003398 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003399 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3400 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3401 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3402 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3403 {
3404 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3405 /* The multiple "Capture Source" controls confuse alsamixer
3406 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003407 */
3408 /* .name = "Capture Source", */
3409 .name = "Input Source",
3410 .count = 2,
3411 .info = ad198x_mux_enum_info,
3412 .get = ad198x_mux_enum_get,
3413 .put = ad198x_mux_enum_put,
3414 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003415 /* SPDIF controls */
3416 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3417 {
3418 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3419 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3420 /* identical with ad1983 */
3421 .info = ad1983_spdif_route_info,
3422 .get = ad1983_spdif_route_get,
3423 .put = ad1983_spdif_route_put,
3424 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003425 { } /* end */
3426};
3427
3428/* additional verbs */
3429static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3430 /* Port-E (docking station mic) pin */
3431 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3432 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3433 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003434 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003435 /* Analog mixer - docking mic; mute as default */
3436 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003437 /* enable EAPD bit */
3438 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003439 { } /* end */
3440};
3441
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003442/*
3443 * Dell Precision T3400
3444 */
3445static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
3446 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3447 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3448 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3449 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3450 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3451 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3452 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3453 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3454 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003455 HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
3456 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3457 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3458 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3459 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3460 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3461 {
3462 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3463 /* The multiple "Capture Source" controls confuse alsamixer
3464 * So call somewhat different..
3465 */
3466 /* .name = "Capture Source", */
3467 .name = "Input Source",
3468 .count = 2,
3469 .info = ad198x_mux_enum_info,
3470 .get = ad198x_mux_enum_get,
3471 .put = ad198x_mux_enum_put,
3472 },
3473 { } /* end */
3474};
3475
Takashi Iwai2bac6472007-05-18 18:21:41 +02003476/* Digial MIC ADC NID 0x05 + 0x06 */
3477static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3478 struct hda_codec *codec,
3479 unsigned int stream_tag,
3480 unsigned int format,
3481 struct snd_pcm_substream *substream)
3482{
3483 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3484 stream_tag, 0, format);
3485 return 0;
3486}
3487
3488static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3489 struct hda_codec *codec,
3490 struct snd_pcm_substream *substream)
3491{
Takashi Iwai888afa12008-03-18 09:57:50 +01003492 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003493 return 0;
3494}
3495
3496static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3497 .substreams = 2,
3498 .channels_min = 2,
3499 .channels_max = 2,
3500 .nid = 0x05,
3501 .ops = {
3502 .prepare = ad1984_pcm_dmic_prepare,
3503 .cleanup = ad1984_pcm_dmic_cleanup
3504 },
3505};
3506
3507static int ad1984_build_pcms(struct hda_codec *codec)
3508{
3509 struct ad198x_spec *spec = codec->spec;
3510 struct hda_pcm *info;
3511 int err;
3512
3513 err = ad198x_build_pcms(codec);
3514 if (err < 0)
3515 return err;
3516
3517 info = spec->pcm_rec + codec->num_pcms;
3518 codec->num_pcms++;
3519 info->name = "AD1984 Digital Mic";
3520 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3521 return 0;
3522}
3523
3524/* models */
3525enum {
3526 AD1984_BASIC,
3527 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003528 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003529 AD1984_MODELS
3530};
3531
3532static const char *ad1984_models[AD1984_MODELS] = {
3533 [AD1984_BASIC] = "basic",
3534 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003535 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003536};
3537
3538static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3539 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003540 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003541 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003542 {}
3543};
3544
3545static int patch_ad1984(struct hda_codec *codec)
3546{
3547 struct ad198x_spec *spec;
3548 int board_config, err;
3549
3550 err = patch_ad1884(codec);
3551 if (err < 0)
3552 return err;
3553 spec = codec->spec;
3554 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3555 ad1984_models, ad1984_cfg_tbl);
3556 switch (board_config) {
3557 case AD1984_BASIC:
3558 /* additional digital mics */
3559 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3560 codec->patch_ops.build_pcms = ad1984_build_pcms;
3561 break;
3562 case AD1984_THINKPAD:
Jerone Youngebf00c52008-01-07 12:22:18 +01003563 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003564 spec->input_mux = &ad1984_thinkpad_capture_source;
3565 spec->mixers[0] = ad1984_thinkpad_mixers;
3566 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
3567 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003568 case AD1984_DELL_DESKTOP:
3569 spec->multiout.dig_out_nid = 0;
3570 spec->input_mux = &ad1984_dell_desktop_capture_source;
3571 spec->mixers[0] = ad1984_dell_desktop_mixers;
3572 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003573 }
3574 return 0;
3575}
3576
3577
3578/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003579 * AD1883 / AD1884A / AD1984A / AD1984B
3580 *
3581 * port-B (0x14) - front mic-in
3582 * port-E (0x1c) - rear mic-in
3583 * port-F (0x16) - CD / ext out
3584 * port-C (0x15) - rear line-in
3585 * port-D (0x12) - rear line-out
3586 * port-A (0x11) - front hp-out
3587 *
3588 * AD1984A = AD1884A + digital-mic
3589 * AD1883 = equivalent with AD1984A
3590 * AD1984B = AD1984A + extra SPDIF-out
3591 *
3592 * FIXME:
3593 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3594 */
3595
3596static hda_nid_t ad1884a_dac_nids[1] = {
3597 0x03,
3598};
3599
3600#define ad1884a_adc_nids ad1884_adc_nids
3601#define ad1884a_capsrc_nids ad1884_capsrc_nids
3602
3603#define AD1884A_SPDIF_OUT 0x02
3604
3605static struct hda_input_mux ad1884a_capture_source = {
3606 .num_items = 5,
3607 .items = {
3608 { "Front Mic", 0x0 },
3609 { "Mic", 0x4 },
3610 { "Line", 0x1 },
3611 { "CD", 0x2 },
3612 { "Mix", 0x3 },
3613 },
3614};
3615
3616static struct snd_kcontrol_new ad1884a_base_mixers[] = {
3617 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3618 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3619 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3620 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3621 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3622 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3623 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3624 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3625 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3626 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3627 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3628 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3629 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3630 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3631 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3632 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003633 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3634 HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
3635 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3636 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3637 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3638 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3639 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3640 {
3641 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3642 /* The multiple "Capture Source" controls confuse alsamixer
3643 * So call somewhat different..
3644 */
3645 /* .name = "Capture Source", */
3646 .name = "Input Source",
3647 .count = 2,
3648 .info = ad198x_mux_enum_info,
3649 .get = ad198x_mux_enum_get,
3650 .put = ad198x_mux_enum_put,
3651 },
3652 /* SPDIF controls */
3653 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3654 {
3655 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3656 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3657 /* identical with ad1983 */
3658 .info = ad1983_spdif_route_info,
3659 .get = ad1983_spdif_route_get,
3660 .put = ad1983_spdif_route_put,
3661 },
3662 { } /* end */
3663};
3664
3665/*
3666 * initialization verbs
3667 */
3668static struct hda_verb ad1884a_init_verbs[] = {
3669 /* DACs; unmute as default */
3670 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3671 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3672 /* Port-A (HP) mixer - route only from analog mixer */
3673 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3674 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3675 /* Port-A pin */
3676 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3677 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3678 /* Port-D (Line-out) mixer - route only from analog mixer */
3679 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3680 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3681 /* Port-D pin */
3682 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3683 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3684 /* Mono-out mixer - route only from analog mixer */
3685 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3686 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3687 /* Mono-out pin */
3688 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3689 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3690 /* Port-B (front mic) pin */
3691 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003692 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003693 /* Port-C (rear line-in) pin */
3694 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003695 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003696 /* Port-E (rear mic) pin */
3697 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3698 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3699 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
3700 /* Port-F (CD) pin */
3701 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3702 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3703 /* Analog mixer; mute as default */
3704 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3705 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3706 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3707 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3708 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
3709 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3710 /* Analog Mix output amp */
3711 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3712 /* capture sources */
3713 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
3714 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3715 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3716 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3717 /* SPDIF output amp */
3718 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3719 { } /* end */
3720};
3721
3722#ifdef CONFIG_SND_HDA_POWER_SAVE
3723static struct hda_amp_list ad1884a_loopbacks[] = {
3724 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3725 { 0x20, HDA_INPUT, 1 }, /* Mic */
3726 { 0x20, HDA_INPUT, 2 }, /* CD */
3727 { 0x20, HDA_INPUT, 4 }, /* Docking */
3728 { } /* end */
3729};
3730#endif
3731
3732/*
3733 * Laptop model
3734 *
3735 * Port A: Headphone jack
3736 * Port B: MIC jack
3737 * Port C: Internal MIC
3738 * Port D: Dock Line Out (if enabled)
3739 * Port E: Dock Line In (if enabled)
3740 * Port F: Internal speakers
3741 */
3742
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003743static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
3744 struct snd_ctl_elem_value *ucontrol)
3745{
3746 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3747 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
3748 int mute = (!ucontrol->value.integer.value[0] &&
3749 !ucontrol->value.integer.value[1]);
3750 /* toggle GPIO1 according to the mute state */
3751 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
3752 mute ? 0x02 : 0x0);
3753 return ret;
3754}
Takashi Iwaic5059252008-02-16 09:43:56 +01003755
3756static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
3757 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003758 {
3759 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3760 .name = "Master Playback Switch",
Jaroslav Kysela9e3fd872009-12-08 17:45:25 +01003761 .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x21,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003762 .info = snd_hda_mixer_amp_switch_info,
3763 .get = snd_hda_mixer_amp_switch_get,
3764 .put = ad1884a_mobile_master_sw_put,
3765 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3766 },
Takashi Iwaic5059252008-02-16 09:43:56 +01003767 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3768 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3769 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3770 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3771 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3772 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3773 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3774 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3775 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003776 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3777 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
3778 HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3779 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3780 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003781 { } /* end */
3782};
3783
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003784static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
3785 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db17e2009-07-02 16:10:23 +02003786 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
3787 {
3788 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3789 .name = "Master Playback Switch",
Jaroslav Kysela9e3fd872009-12-08 17:45:25 +01003790 .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x21,
Takashi Iwai099db17e2009-07-02 16:10:23 +02003791 .info = snd_hda_mixer_amp_switch_info,
3792 .get = snd_hda_mixer_amp_switch_get,
3793 .put = ad1884a_mobile_master_sw_put,
3794 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3795 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003796 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3797 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02003798 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
3799 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003800 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3801 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003802 { } /* end */
3803};
3804
Takashi Iwaic5059252008-02-16 09:43:56 +01003805/* mute internal speaker if HP is plugged */
3806static void ad1884a_hp_automute(struct hda_codec *codec)
3807{
3808 unsigned int present;
3809
Takashi Iwaid56757a2009-11-18 08:00:14 +01003810 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01003811 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3812 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3813 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3814 present ? 0x00 : 0x02);
3815}
3816
Takashi Iwai269ef192008-05-30 15:32:15 +02003817/* switch to external mic if plugged */
3818static void ad1884a_hp_automic(struct hda_codec *codec)
3819{
3820 unsigned int present;
3821
Takashi Iwaid56757a2009-11-18 08:00:14 +01003822 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02003823 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
3824 present ? 0 : 1);
3825}
3826
Takashi Iwaic5059252008-02-16 09:43:56 +01003827#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02003828#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01003829
3830/* unsolicited event for HP jack sensing */
3831static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
3832{
Takashi Iwai269ef192008-05-30 15:32:15 +02003833 switch (res >> 26) {
3834 case AD1884A_HP_EVENT:
3835 ad1884a_hp_automute(codec);
3836 break;
3837 case AD1884A_MIC_EVENT:
3838 ad1884a_hp_automic(codec);
3839 break;
3840 }
Takashi Iwaic5059252008-02-16 09:43:56 +01003841}
3842
3843/* initialize jack-sensing, too */
3844static int ad1884a_hp_init(struct hda_codec *codec)
3845{
3846 ad198x_init(codec);
3847 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02003848 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01003849 return 0;
3850}
3851
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003852/* mute internal speaker if HP or docking HP is plugged */
3853static void ad1884a_laptop_automute(struct hda_codec *codec)
3854{
3855 unsigned int present;
3856
Takashi Iwaid56757a2009-11-18 08:00:14 +01003857 present = snd_hda_jack_detect(codec, 0x11);
3858 if (!present)
3859 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003860 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3861 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3862 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3863 present ? 0x00 : 0x02);
3864}
3865
3866/* switch to external mic if plugged */
3867static void ad1884a_laptop_automic(struct hda_codec *codec)
3868{
3869 unsigned int idx;
3870
Takashi Iwaid56757a2009-11-18 08:00:14 +01003871 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003872 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01003873 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003874 idx = 4;
3875 else
3876 idx = 1;
3877 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
3878}
3879
3880/* unsolicited event for HP jack sensing */
3881static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
3882 unsigned int res)
3883{
3884 switch (res >> 26) {
3885 case AD1884A_HP_EVENT:
3886 ad1884a_laptop_automute(codec);
3887 break;
3888 case AD1884A_MIC_EVENT:
3889 ad1884a_laptop_automic(codec);
3890 break;
3891 }
3892}
3893
3894/* initialize jack-sensing, too */
3895static int ad1884a_laptop_init(struct hda_codec *codec)
3896{
3897 ad198x_init(codec);
3898 ad1884a_laptop_automute(codec);
3899 ad1884a_laptop_automic(codec);
3900 return 0;
3901}
3902
Takashi Iwaic5059252008-02-16 09:43:56 +01003903/* additional verbs for laptop model */
3904static struct hda_verb ad1884a_laptop_verbs[] = {
3905 /* Port-A (HP) pin - always unmuted */
3906 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3907 /* Port-F (int speaker) mixer - route only from analog mixer */
3908 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3909 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08003910 /* Port-F (int speaker) pin */
3911 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01003912 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08003913 /* required for compaq 6530s/6531s speaker output */
3914 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003915 /* Port-C pin - internal mic-in */
3916 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3917 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3918 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02003919 /* Port-D (docking line-out) pin - default unmuted */
3920 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01003921 /* analog mix */
3922 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3923 /* unsolicited event for pin-sense */
3924 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003925 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003926 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003927 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02003928 /* allow to touch GPIO1 (for mute control) */
3929 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
3930 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
3931 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01003932 { } /* end */
3933};
3934
Takashi Iwai73156132009-04-23 08:24:48 +02003935static struct hda_verb ad1884a_mobile_verbs[] = {
3936 /* DACs; unmute as default */
3937 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3938 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3939 /* Port-A (HP) mixer - route only from analog mixer */
3940 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3941 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3942 /* Port-A pin */
3943 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3944 /* Port-A (HP) pin - always unmuted */
3945 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3946 /* Port-B (mic jack) pin */
3947 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3948 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3949 /* Port-C (int mic) pin */
3950 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3951 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3952 /* Port-F (int speaker) mixer - route only from analog mixer */
3953 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3954 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3955 /* Port-F pin */
3956 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3957 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3958 /* Analog mixer; mute as default */
3959 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3960 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3961 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3962 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3963 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3964 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3965 /* Analog Mix output amp */
3966 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3967 /* capture sources */
3968 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
3969 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3970 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3971 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3972 /* unsolicited event for pin-sense */
3973 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
3974 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db17e2009-07-02 16:10:23 +02003975 /* allow to touch GPIO1 (for mute control) */
3976 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
3977 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
3978 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02003979 { } /* end */
3980};
3981
Takashi Iwaic5059252008-02-16 09:43:56 +01003982/*
Takashi Iwaif0813742008-03-18 12:13:03 +01003983 * Thinkpad X300
3984 * 0x11 - HP
3985 * 0x12 - speaker
3986 * 0x14 - mic-in
3987 * 0x17 - built-in mic
3988 */
3989
3990static struct hda_verb ad1984a_thinkpad_verbs[] = {
3991 /* HP unmute */
3992 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3993 /* analog mix */
3994 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3995 /* turn on EAPD */
3996 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
3997 /* unsolicited event for pin-sense */
3998 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
3999 /* internal mic - dmic */
4000 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02004001 /* set magic COEFs for dmic */
4002 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4003 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01004004 { } /* end */
4005};
4006
4007static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
4008 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4009 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4010 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4011 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4012 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4013 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01004014 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
4015 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4016 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4017 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4018 {
4019 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4020 .name = "Capture Source",
4021 .info = ad198x_mux_enum_info,
4022 .get = ad198x_mux_enum_get,
4023 .put = ad198x_mux_enum_put,
4024 },
4025 { } /* end */
4026};
4027
4028static struct hda_input_mux ad1984a_thinkpad_capture_source = {
4029 .num_items = 3,
4030 .items = {
4031 { "Mic", 0x0 },
4032 { "Internal Mic", 0x5 },
4033 { "Mix", 0x3 },
4034 },
4035};
4036
4037/* mute internal speaker if HP is plugged */
4038static void ad1984a_thinkpad_automute(struct hda_codec *codec)
4039{
4040 unsigned int present;
4041
Takashi Iwaid56757a2009-11-18 08:00:14 +01004042 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01004043 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
4044 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4045}
4046
4047/* unsolicited event for HP jack sensing */
4048static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
4049 unsigned int res)
4050{
4051 if ((res >> 26) != AD1884A_HP_EVENT)
4052 return;
4053 ad1984a_thinkpad_automute(codec);
4054}
4055
4056/* initialize jack-sensing, too */
4057static int ad1984a_thinkpad_init(struct hda_codec *codec)
4058{
4059 ad198x_init(codec);
4060 ad1984a_thinkpad_automute(codec);
4061 return 0;
4062}
4063
4064/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004065 * HP Touchsmart
4066 * port-A (0x11) - front hp-out
4067 * port-B (0x14) - unused
4068 * port-C (0x15) - unused
4069 * port-D (0x12) - rear line out
4070 * port-E (0x1c) - front mic-in
4071 * port-F (0x16) - Internal speakers
4072 * digital-mic (0x17) - Internal mic
4073 */
4074
4075static struct hda_verb ad1984a_touchsmart_verbs[] = {
4076 /* DACs; unmute as default */
4077 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4078 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4079 /* Port-A (HP) mixer - route only from analog mixer */
4080 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4081 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4082 /* Port-A pin */
4083 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4084 /* Port-A (HP) pin - always unmuted */
4085 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4086 /* Port-E (int speaker) mixer - route only from analog mixer */
4087 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4088 /* Port-E pin */
4089 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4090 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4091 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4092 /* Port-F (int speaker) mixer - route only from analog mixer */
4093 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4094 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4095 /* Port-F pin */
4096 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4097 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4098 /* Analog mixer; mute as default */
4099 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4100 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4101 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4102 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4103 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4104 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4105 /* Analog Mix output amp */
4106 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4107 /* capture sources */
4108 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4109 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4110 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4111 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4112 /* unsolicited event for pin-sense */
4113 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4114 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4115 /* allow to touch GPIO1 (for mute control) */
4116 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4117 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4118 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4119 /* internal mic - dmic */
4120 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4121 /* set magic COEFs for dmic */
4122 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4123 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4124 { } /* end */
4125};
4126
4127static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
4128 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4129/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4130 {
4131 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela9e3fd872009-12-08 17:45:25 +01004132 .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x21,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004133 .name = "Master Playback Switch",
4134 .info = snd_hda_mixer_amp_switch_info,
4135 .get = snd_hda_mixer_amp_switch_get,
4136 .put = ad1884a_mobile_master_sw_put,
4137 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4138 },
4139 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4140 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4141 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4142 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4143 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
4144 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4145 { } /* end */
4146};
4147
4148/* switch to external mic if plugged */
4149static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4150{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004151 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004152 snd_hda_codec_write(codec, 0x0c, 0,
4153 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004154 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004155 snd_hda_codec_write(codec, 0x0c, 0,
4156 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004157}
4158
4159
4160/* unsolicited event for HP jack sensing */
4161static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4162 unsigned int res)
4163{
4164 switch (res >> 26) {
4165 case AD1884A_HP_EVENT:
4166 ad1884a_hp_automute(codec);
4167 break;
4168 case AD1884A_MIC_EVENT:
4169 ad1984a_touchsmart_automic(codec);
4170 break;
4171 }
4172}
4173
4174/* initialize jack-sensing, too */
4175static int ad1984a_touchsmart_init(struct hda_codec *codec)
4176{
4177 ad198x_init(codec);
4178 ad1884a_hp_automute(codec);
4179 ad1984a_touchsmart_automic(codec);
4180 return 0;
4181}
4182
4183
4184/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004185 */
4186
4187enum {
4188 AD1884A_DESKTOP,
4189 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004190 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004191 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004192 AD1984A_TOUCHSMART,
Takashi Iwaic5059252008-02-16 09:43:56 +01004193 AD1884A_MODELS
4194};
4195
4196static const char *ad1884a_models[AD1884A_MODELS] = {
4197 [AD1884A_DESKTOP] = "desktop",
4198 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004199 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004200 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004201 [AD1984A_TOUCHSMART] = "touchsmart",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004202};
4203
4204static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
4205 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004206 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004207 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004208 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004209 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004210 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4211 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004212 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004213 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004214 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004215 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004216};
4217
4218static int patch_ad1884a(struct hda_codec *codec)
4219{
4220 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004221 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004222
4223 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4224 if (spec == NULL)
4225 return -ENOMEM;
4226
Takashi Iwaic5059252008-02-16 09:43:56 +01004227 codec->spec = spec;
4228
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004229 err = snd_hda_attach_beep_device(codec, 0x10);
4230 if (err < 0) {
4231 ad198x_free(codec);
4232 return err;
4233 }
4234 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4235
Takashi Iwaic5059252008-02-16 09:43:56 +01004236 spec->multiout.max_channels = 2;
4237 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4238 spec->multiout.dac_nids = ad1884a_dac_nids;
4239 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4240 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4241 spec->adc_nids = ad1884a_adc_nids;
4242 spec->capsrc_nids = ad1884a_capsrc_nids;
4243 spec->input_mux = &ad1884a_capture_source;
4244 spec->num_mixers = 1;
4245 spec->mixers[0] = ad1884a_base_mixers;
4246 spec->num_init_verbs = 1;
4247 spec->init_verbs[0] = ad1884a_init_verbs;
4248 spec->spdif_route = 0;
4249#ifdef CONFIG_SND_HDA_POWER_SAVE
4250 spec->loopback.amplist = ad1884a_loopbacks;
4251#endif
4252 codec->patch_ops = ad198x_patch_ops;
4253
4254 /* override some parameters */
4255 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004256 ad1884a_models,
4257 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01004258 switch (board_config) {
4259 case AD1884A_LAPTOP:
4260 spec->mixers[0] = ad1884a_laptop_mixers;
4261 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4262 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004263 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4264 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004265 /* set the upper-limit for mixer amp to 0dB for avoiding the
4266 * possible damage by overloading
4267 */
4268 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4269 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4270 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4271 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4272 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004273 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004274 case AD1884A_MOBILE:
4275 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004276 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004277 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004278 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4279 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004280 /* set the upper-limit for mixer amp to 0dB for avoiding the
4281 * possible damage by overloading
4282 */
4283 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4284 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4285 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4286 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4287 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004288 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004289 case AD1884A_THINKPAD:
4290 spec->mixers[0] = ad1984a_thinkpad_mixers;
4291 spec->init_verbs[spec->num_init_verbs++] =
4292 ad1984a_thinkpad_verbs;
4293 spec->multiout.dig_out_nid = 0;
4294 spec->input_mux = &ad1984a_thinkpad_capture_source;
4295 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4296 codec->patch_ops.init = ad1984a_thinkpad_init;
4297 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004298 case AD1984A_TOUCHSMART:
4299 spec->mixers[0] = ad1984a_touchsmart_mixers;
4300 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4301 spec->multiout.dig_out_nid = 0;
4302 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4303 codec->patch_ops.init = ad1984a_touchsmart_init;
4304 /* set the upper-limit for mixer amp to 0dB for avoiding the
4305 * possible damage by overloading
4306 */
4307 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4308 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4309 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4310 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4311 (1 << AC_AMPCAP_MUTE_SHIFT));
4312 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004313 }
4314
4315 return 0;
4316}
4317
4318
4319/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004320 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004321 *
4322 * port-A - front hp-out
4323 * port-B - front mic-in
4324 * port-C - rear line-in, shared surr-out (3stack)
4325 * port-D - rear line-out
4326 * port-E - rear mic-in, shared clfe-out (3stack)
4327 * port-F - rear surr-out (6stack)
4328 * port-G - rear clfe-out (6stack)
4329 */
4330
4331static hda_nid_t ad1882_dac_nids[3] = {
4332 0x04, 0x03, 0x05
4333};
4334
4335static hda_nid_t ad1882_adc_nids[2] = {
4336 0x08, 0x09,
4337};
4338
4339static hda_nid_t ad1882_capsrc_nids[2] = {
4340 0x0c, 0x0d,
4341};
4342
4343#define AD1882_SPDIF_OUT 0x02
4344
4345/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
4346static struct hda_input_mux ad1882_capture_source = {
4347 .num_items = 5,
4348 .items = {
4349 { "Front Mic", 0x1 },
4350 { "Mic", 0x4 },
4351 { "Line", 0x2 },
4352 { "CD", 0x3 },
4353 { "Mix", 0x7 },
4354 },
4355};
4356
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004357/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
4358static struct hda_input_mux ad1882a_capture_source = {
4359 .num_items = 5,
4360 .items = {
4361 { "Front Mic", 0x1 },
4362 { "Mic", 0x4},
4363 { "Line", 0x2 },
4364 { "Digital Mic", 0x06 },
4365 { "Mix", 0x7 },
4366 },
4367};
4368
Takashi Iwai0ac85512007-06-20 15:46:13 +02004369static struct snd_kcontrol_new ad1882_base_mixers[] = {
4370 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4371 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4372 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4373 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4374 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4375 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4376 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4377 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004378
Takashi Iwai0ac85512007-06-20 15:46:13 +02004379 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
4380 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
4381 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
4382 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4383 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4384 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4385 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4386 {
4387 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4388 /* The multiple "Capture Source" controls confuse alsamixer
4389 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004390 */
4391 /* .name = "Capture Source", */
4392 .name = "Input Source",
4393 .count = 2,
4394 .info = ad198x_mux_enum_info,
4395 .get = ad198x_mux_enum_get,
4396 .put = ad198x_mux_enum_put,
4397 },
4398 /* SPDIF controls */
4399 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4400 {
4401 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4402 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4403 /* identical with ad1983 */
4404 .info = ad1983_spdif_route_info,
4405 .get = ad1983_spdif_route_get,
4406 .put = ad1983_spdif_route_put,
4407 },
4408 { } /* end */
4409};
4410
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004411static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
4412 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4413 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4414 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4415 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4416 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4417 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4418 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4419 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004420 { } /* end */
4421};
4422
4423static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
4424 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4425 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4426 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4427 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4428 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4429 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4430 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4431 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004432 HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
4433 { } /* end */
4434};
4435
Takashi Iwai0ac85512007-06-20 15:46:13 +02004436static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
4437 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4438 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4439 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4440 {
4441 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4442 .name = "Channel Mode",
4443 .info = ad198x_ch_mode_info,
4444 .get = ad198x_ch_mode_get,
4445 .put = ad198x_ch_mode_put,
4446 },
4447 { } /* end */
4448};
4449
4450static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
4451 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4452 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4453 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4454 { } /* end */
4455};
4456
4457static struct hda_verb ad1882_ch2_init[] = {
4458 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4459 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4460 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4461 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4462 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4463 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4464 { } /* end */
4465};
4466
4467static struct hda_verb ad1882_ch4_init[] = {
4468 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4469 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4470 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4471 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4472 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4473 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4474 { } /* end */
4475};
4476
4477static struct hda_verb ad1882_ch6_init[] = {
4478 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4479 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4480 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4481 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4482 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4483 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4484 { } /* end */
4485};
4486
4487static struct hda_channel_mode ad1882_modes[3] = {
4488 { 2, ad1882_ch2_init },
4489 { 4, ad1882_ch4_init },
4490 { 6, ad1882_ch6_init },
4491};
4492
4493/*
4494 * initialization verbs
4495 */
4496static struct hda_verb ad1882_init_verbs[] = {
4497 /* DACs; mute as default */
4498 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4499 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4500 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4501 /* Port-A (HP) mixer */
4502 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4503 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4504 /* Port-A pin */
4505 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4506 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4507 /* HP selector - select DAC2 */
4508 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4509 /* Port-D (Line-out) mixer */
4510 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4511 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4512 /* Port-D pin */
4513 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4514 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4515 /* Mono-out mixer */
4516 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4517 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4518 /* Mono-out pin */
4519 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4520 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4521 /* Port-B (front mic) pin */
4522 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4523 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4524 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4525 /* Port-C (line-in) pin */
4526 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4527 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4528 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4529 /* Port-C mixer - mute as input */
4530 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4531 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4532 /* Port-E (mic-in) pin */
4533 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4534 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4535 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4536 /* Port-E mixer - mute as input */
4537 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4538 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4539 /* Port-F (surround) */
4540 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4541 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4542 /* Port-G (CLFE) */
4543 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4544 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4545 /* Analog mixer; mute as default */
4546 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4547 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4548 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4549 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4550 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4551 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4552 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4553 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4554 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4555 /* Analog Mix output amp */
4556 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4557 /* SPDIF output selector */
4558 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4559 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4560 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4561 { } /* end */
4562};
4563
Takashi Iwaicb53c622007-08-10 17:21:45 +02004564#ifdef CONFIG_SND_HDA_POWER_SAVE
4565static struct hda_amp_list ad1882_loopbacks[] = {
4566 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4567 { 0x20, HDA_INPUT, 1 }, /* Mic */
4568 { 0x20, HDA_INPUT, 4 }, /* Line */
4569 { 0x20, HDA_INPUT, 6 }, /* CD */
4570 { } /* end */
4571};
4572#endif
4573
Takashi Iwai0ac85512007-06-20 15:46:13 +02004574/* models */
4575enum {
4576 AD1882_3STACK,
4577 AD1882_6STACK,
4578 AD1882_MODELS
4579};
4580
4581static const char *ad1882_models[AD1986A_MODELS] = {
4582 [AD1882_3STACK] = "3stack",
4583 [AD1882_6STACK] = "6stack",
4584};
4585
4586
4587static int patch_ad1882(struct hda_codec *codec)
4588{
4589 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004590 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004591
4592 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4593 if (spec == NULL)
4594 return -ENOMEM;
4595
Takashi Iwai0ac85512007-06-20 15:46:13 +02004596 codec->spec = spec;
4597
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004598 err = snd_hda_attach_beep_device(codec, 0x10);
4599 if (err < 0) {
4600 ad198x_free(codec);
4601 return err;
4602 }
4603 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4604
Takashi Iwai0ac85512007-06-20 15:46:13 +02004605 spec->multiout.max_channels = 6;
4606 spec->multiout.num_dacs = 3;
4607 spec->multiout.dac_nids = ad1882_dac_nids;
4608 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
4609 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
4610 spec->adc_nids = ad1882_adc_nids;
4611 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004612 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004613 spec->input_mux = &ad1882_capture_source;
4614 else
4615 spec->input_mux = &ad1882a_capture_source;
4616 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004617 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004618 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004619 spec->mixers[1] = ad1882_loopback_mixers;
4620 else
4621 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004622 spec->num_init_verbs = 1;
4623 spec->init_verbs[0] = ad1882_init_verbs;
4624 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02004625#ifdef CONFIG_SND_HDA_POWER_SAVE
4626 spec->loopback.amplist = ad1882_loopbacks;
4627#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01004628 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004629
4630 codec->patch_ops = ad198x_patch_ops;
4631
4632 /* override some parameters */
4633 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
4634 ad1882_models, NULL);
4635 switch (board_config) {
4636 default:
4637 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004638 spec->num_mixers = 3;
4639 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004640 spec->channel_mode = ad1882_modes;
4641 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
4642 spec->need_dac_fix = 1;
4643 spec->multiout.max_channels = 2;
4644 spec->multiout.num_dacs = 1;
4645 break;
4646 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004647 spec->num_mixers = 3;
4648 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004649 break;
4650 }
4651 return 0;
4652}
4653
4654
4655/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004656 * patch entries
4657 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004658static struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004659 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02004660 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004661 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004662 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004663 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
4664 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02004665 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
4666 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004667 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004668 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01004669 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02004670 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004671 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02004672 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
4673 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004674 {} /* terminator */
4675};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004676
4677MODULE_ALIAS("snd-hda-codec-id:11d4*");
4678
4679MODULE_LICENSE("GPL");
4680MODULE_DESCRIPTION("Analog Devices HD-audio codec");
4681
4682static struct hda_codec_preset_list analog_list = {
4683 .preset = snd_hda_preset_analog,
4684 .owner = THIS_MODULE,
4685};
4686
4687static int __init patch_analog_init(void)
4688{
4689 return snd_hda_add_codec_preset(&analog_list);
4690}
4691
4692static void __exit patch_analog_exit(void)
4693{
4694 snd_hda_delete_codec_preset(&analog_list);
4695}
4696
4697module_init(patch_analog_init)
4698module_exit(patch_analog_exit)