blob: d418842373fd5e71cc6bda8063c0b53c74f9b5d5 [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 Kysela5b0cb1d2009-12-08 16:13:32 +0100835 .subdevice = HDA_SUBDEV_NID_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))
Takashi Iwai9c96fa52009-11-16 11:25:33 +01002605 knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002606 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002607 return 0;
2608}
2609
2610#define AD1988_PIN_CD_NID 0x18
2611#define AD1988_PIN_BEEP_NID 0x10
2612
2613static hda_nid_t ad1988_mixer_nids[8] = {
2614 /* A B C D E F G H */
2615 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2616};
2617
2618static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2619{
2620 static hda_nid_t idx_to_dac[8] = {
2621 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002622 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002623 };
2624 static hda_nid_t idx_to_dac_rev2[8] = {
2625 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002626 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002627 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002628 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002629 return idx_to_dac_rev2[idx];
2630 else
2631 return idx_to_dac[idx];
2632}
2633
2634static hda_nid_t ad1988_boost_nids[8] = {
2635 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2636};
2637
2638static int ad1988_pin_idx(hda_nid_t nid)
2639{
2640 static hda_nid_t ad1988_io_pins[8] = {
2641 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2642 };
2643 int i;
2644 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2645 if (ad1988_io_pins[i] == nid)
2646 return i;
2647 return 0; /* should be -1 */
2648}
2649
2650static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2651{
2652 static int loopback_idx[8] = {
2653 2, 0, 1, 3, 4, 5, 1, 4
2654 };
2655 switch (nid) {
2656 case AD1988_PIN_CD_NID:
2657 return 6;
2658 default:
2659 return loopback_idx[ad1988_pin_idx(nid)];
2660 }
2661}
2662
2663static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2664{
2665 static int adc_idx[8] = {
2666 0, 1, 2, 8, 4, 3, 6, 7
2667 };
2668 switch (nid) {
2669 case AD1988_PIN_CD_NID:
2670 return 5;
2671 default:
2672 return adc_idx[ad1988_pin_idx(nid)];
2673 }
2674}
2675
2676/* fill in the dac_nids table from the parsed pin configuration */
2677static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2678 const struct auto_pin_cfg *cfg)
2679{
2680 struct ad198x_spec *spec = codec->spec;
2681 int i, idx;
2682
2683 spec->multiout.dac_nids = spec->private_dac_nids;
2684
2685 /* check the pins hardwired to audio widget */
2686 for (i = 0; i < cfg->line_outs; i++) {
2687 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2688 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2689 }
2690 spec->multiout.num_dacs = cfg->line_outs;
2691 return 0;
2692}
2693
2694/* add playback controls from the parsed DAC table */
2695static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2696 const struct auto_pin_cfg *cfg)
2697{
2698 char name[32];
2699 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2700 hda_nid_t nid;
2701 int i, err;
2702
2703 for (i = 0; i < cfg->line_outs; i++) {
2704 hda_nid_t dac = spec->multiout.dac_nids[i];
2705 if (! dac)
2706 continue;
2707 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2708 if (i == 2) {
2709 /* Center/LFE */
2710 err = add_control(spec, AD_CTL_WIDGET_VOL,
2711 "Center Playback Volume",
2712 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2713 if (err < 0)
2714 return err;
2715 err = add_control(spec, AD_CTL_WIDGET_VOL,
2716 "LFE Playback Volume",
2717 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2718 if (err < 0)
2719 return err;
2720 err = add_control(spec, AD_CTL_BIND_MUTE,
2721 "Center Playback Switch",
2722 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2723 if (err < 0)
2724 return err;
2725 err = add_control(spec, AD_CTL_BIND_MUTE,
2726 "LFE Playback Switch",
2727 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2728 if (err < 0)
2729 return err;
2730 } else {
2731 sprintf(name, "%s Playback Volume", chname[i]);
2732 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2733 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2734 if (err < 0)
2735 return err;
2736 sprintf(name, "%s Playback Switch", chname[i]);
2737 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2738 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2739 if (err < 0)
2740 return err;
2741 }
2742 }
2743 return 0;
2744}
2745
2746/* add playback controls for speaker and HP outputs */
2747static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2748 const char *pfx)
2749{
2750 struct ad198x_spec *spec = codec->spec;
2751 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02002752 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002753 char name[32];
2754
2755 if (! pin)
2756 return 0;
2757
2758 idx = ad1988_pin_idx(pin);
2759 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02002760 /* check whether the corresponding DAC was already taken */
2761 for (i = 0; i < spec->autocfg.line_outs; i++) {
2762 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2763 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
2764 if (dac == nid)
2765 break;
2766 }
2767 if (i >= spec->autocfg.line_outs) {
2768 /* specify the DAC as the extra output */
2769 if (!spec->multiout.hp_nid)
2770 spec->multiout.hp_nid = nid;
2771 else
2772 spec->multiout.extra_out_nid[0] = nid;
2773 /* control HP volume/switch on the output mixer amp */
2774 sprintf(name, "%s Playback Volume", pfx);
2775 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2776 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2777 if (err < 0)
2778 return err;
2779 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01002780 nid = ad1988_mixer_nids[idx];
2781 sprintf(name, "%s Playback Switch", pfx);
2782 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2783 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2784 return err;
2785 return 0;
2786}
2787
2788/* create input playback/capture controls for the given pin */
2789static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2790 const char *ctlname, int boost)
2791{
2792 char name[32];
2793 int err, idx;
2794
2795 sprintf(name, "%s Playback Volume", ctlname);
2796 idx = ad1988_pin_to_loopback_idx(pin);
2797 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2798 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2799 return err;
2800 sprintf(name, "%s Playback Switch", ctlname);
2801 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2802 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2803 return err;
2804 if (boost) {
2805 hda_nid_t bnid;
2806 idx = ad1988_pin_idx(pin);
2807 bnid = ad1988_boost_nids[idx];
2808 if (bnid) {
2809 sprintf(name, "%s Boost", ctlname);
2810 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2811 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2812
2813 }
2814 }
2815 return 0;
2816}
2817
2818/* create playback/capture controls for input pins */
2819static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2820 const struct auto_pin_cfg *cfg)
2821{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002822 struct hda_input_mux *imux = &spec->private_imux;
2823 int i, err;
2824
2825 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002826 err = new_analog_input(spec, cfg->input_pins[i],
2827 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002828 i <= AUTO_PIN_FRONT_MIC);
2829 if (err < 0)
2830 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002831 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002832 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2833 imux->num_items++;
2834 }
2835 imux->items[imux->num_items].label = "Mix";
2836 imux->items[imux->num_items].index = 9;
2837 imux->num_items++;
2838
2839 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2840 "Analog Mix Playback Volume",
2841 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2842 return err;
2843 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2844 "Analog Mix Playback Switch",
2845 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2846 return err;
2847
2848 return 0;
2849}
2850
2851static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2852 hda_nid_t nid, int pin_type,
2853 int dac_idx)
2854{
2855 /* set as output */
2856 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2857 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2858 switch (nid) {
2859 case 0x11: /* port-A - DAC 04 */
2860 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2861 break;
2862 case 0x14: /* port-B - DAC 06 */
2863 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2864 break;
2865 case 0x15: /* port-C - DAC 05 */
2866 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2867 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002868 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002869 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2870 break;
2871 case 0x13: /* mono - DAC 04 */
2872 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2873 break;
2874 }
2875}
2876
2877static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2878{
2879 struct ad198x_spec *spec = codec->spec;
2880 int i;
2881
2882 for (i = 0; i < spec->autocfg.line_outs; i++) {
2883 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2884 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2885 }
2886}
2887
2888static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2889{
2890 struct ad198x_spec *spec = codec->spec;
2891 hda_nid_t pin;
2892
Takashi Iwai82bc9552006-03-21 11:24:42 +01002893 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002894 if (pin) /* connect to front */
2895 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002896 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002897 if (pin) /* connect to front */
2898 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2899}
2900
2901static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2902{
2903 struct ad198x_spec *spec = codec->spec;
2904 int i, idx;
2905
2906 for (i = 0; i < AUTO_PIN_LAST; i++) {
2907 hda_nid_t nid = spec->autocfg.input_pins[i];
2908 if (! nid)
2909 continue;
2910 switch (nid) {
2911 case 0x15: /* port-C */
2912 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2913 break;
2914 case 0x17: /* port-E */
2915 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2916 break;
2917 }
2918 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2919 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2920 if (nid != AD1988_PIN_CD_NID)
2921 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
2922 AMP_OUT_MUTE);
2923 idx = ad1988_pin_idx(nid);
2924 if (ad1988_boost_nids[idx])
2925 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
2926 AC_VERB_SET_AMP_GAIN_MUTE,
2927 AMP_OUT_ZERO);
2928 }
2929}
2930
2931/* parse the BIOS configuration and set up the alc_spec */
2932/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
2933static int ad1988_parse_auto_config(struct hda_codec *codec)
2934{
2935 struct ad198x_spec *spec = codec->spec;
2936 int err;
2937
Kailang Yangdf694da2005-12-05 19:42:22 +01002938 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002939 return err;
2940 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
2941 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002942 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002943 return 0; /* can't find valid BIOS pin config */
2944 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01002945 (err = ad1988_auto_create_extra_out(codec,
2946 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002947 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002948 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002949 "Headphone")) < 0 ||
2950 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
2951 return err;
2952
2953 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2954
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002955 if (spec->autocfg.dig_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002956 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2957 if (spec->autocfg.dig_in_pin)
2958 spec->dig_in_nid = AD1988_SPDIF_IN;
2959
Takashi Iwai603c4012008-07-30 15:01:44 +02002960 if (spec->kctls.list)
2961 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002962
2963 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
2964
2965 spec->input_mux = &spec->private_imux;
2966
2967 return 1;
2968}
2969
2970/* init callback for auto-configuration model -- overriding the default init */
2971static int ad1988_auto_init(struct hda_codec *codec)
2972{
2973 ad198x_init(codec);
2974 ad1988_auto_init_multi_out(codec);
2975 ad1988_auto_init_extra_out(codec);
2976 ad1988_auto_init_analog_input(codec);
2977 return 0;
2978}
2979
2980
2981/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002982 */
2983
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002984static const char *ad1988_models[AD1988_MODEL_LAST] = {
2985 [AD1988_6STACK] = "6stack",
2986 [AD1988_6STACK_DIG] = "6stack-dig",
2987 [AD1988_3STACK] = "3stack",
2988 [AD1988_3STACK_DIG] = "3stack-dig",
2989 [AD1988_LAPTOP] = "laptop",
2990 [AD1988_LAPTOP_DIG] = "laptop-dig",
2991 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002992};
2993
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002994static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002995 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002996 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02002997 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07002998 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002999 {}
3000};
3001
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003002static int patch_ad1988(struct hda_codec *codec)
3003{
3004 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003005 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003006
3007 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3008 if (spec == NULL)
3009 return -ENOMEM;
3010
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003011 codec->spec = spec;
3012
Takashi Iwai1a806f42006-07-03 15:58:16 +02003013 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003014 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
3015
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003016 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003017 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003018 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02003019 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3020 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003021 board_config = AD1988_AUTO;
3022 }
3023
3024 if (board_config == AD1988_AUTO) {
3025 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003026 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003027 if (err < 0) {
3028 ad198x_free(codec);
3029 return err;
3030 } else if (! err) {
3031 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
3032 board_config = AD1988_6STACK;
3033 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003034 }
3035
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003036 err = snd_hda_attach_beep_device(codec, 0x10);
3037 if (err < 0) {
3038 ad198x_free(codec);
3039 return err;
3040 }
3041 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3042
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003043 switch (board_config) {
3044 case AD1988_6STACK:
3045 case AD1988_6STACK_DIG:
3046 spec->multiout.max_channels = 8;
3047 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003048 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003049 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3050 else
3051 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003052 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003053 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003054 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003055 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3056 else
3057 spec->mixers[0] = ad1988_6stack_mixers1;
3058 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003059 spec->num_init_verbs = 1;
3060 spec->init_verbs[0] = ad1988_6stack_init_verbs;
3061 if (board_config == AD1988_6STACK_DIG) {
3062 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3063 spec->dig_in_nid = AD1988_SPDIF_IN;
3064 }
3065 break;
3066 case AD1988_3STACK:
3067 case AD1988_3STACK_DIG:
3068 spec->multiout.max_channels = 6;
3069 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003070 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003071 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3072 else
3073 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003074 spec->input_mux = &ad1988_6stack_capture_source;
3075 spec->channel_mode = ad1988_3stack_modes;
3076 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003077 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003078 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003079 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3080 else
3081 spec->mixers[0] = ad1988_3stack_mixers1;
3082 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003083 spec->num_init_verbs = 1;
3084 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3085 if (board_config == AD1988_3STACK_DIG)
3086 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3087 break;
3088 case AD1988_LAPTOP:
3089 case AD1988_LAPTOP_DIG:
3090 spec->multiout.max_channels = 2;
3091 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003092 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003093 spec->input_mux = &ad1988_laptop_capture_source;
3094 spec->num_mixers = 1;
3095 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01003096 spec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003097 spec->num_init_verbs = 1;
3098 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3099 if (board_config == AD1988_LAPTOP_DIG)
3100 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3101 break;
3102 }
3103
Takashi Iwaid32410b12005-11-24 16:06:23 +01003104 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3105 spec->adc_nids = ad1988_adc_nids;
3106 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003107 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3108 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3109 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003110 if (codec->vendor_id >= 0x11d4989a) {
3111 spec->mixers[spec->num_mixers++] =
3112 ad1989_spdif_out_mixers;
3113 spec->init_verbs[spec->num_init_verbs++] =
3114 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003115 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003116 } else {
3117 spec->mixers[spec->num_mixers++] =
3118 ad1988_spdif_out_mixers;
3119 spec->init_verbs[spec->num_init_verbs++] =
3120 ad1988_spdif_init_verbs;
3121 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003122 }
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003123 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003124 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
3125
3126 codec->patch_ops = ad198x_patch_ops;
3127 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003128 case AD1988_AUTO:
3129 codec->patch_ops.init = ad1988_auto_init;
3130 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003131 case AD1988_LAPTOP:
3132 case AD1988_LAPTOP_DIG:
3133 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3134 break;
3135 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003136#ifdef CONFIG_SND_HDA_POWER_SAVE
3137 spec->loopback.amplist = ad1988_loopbacks;
3138#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003139 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003140
3141 return 0;
3142}
3143
3144
3145/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003146 * AD1884 / AD1984
3147 *
3148 * port-B - front line/mic-in
3149 * port-E - aux in/out
3150 * port-F - aux in/out
3151 * port-C - rear line/mic-in
3152 * port-D - rear line/hp-out
3153 * port-A - front line/hp-out
3154 *
3155 * AD1984 = AD1884 + two digital mic-ins
3156 *
3157 * FIXME:
3158 * For simplicity, we share the single DAC for both HP and line-outs
3159 * right now. The inidividual playbacks could be easily implemented,
3160 * but no build-up framework is given, so far.
3161 */
3162
3163static hda_nid_t ad1884_dac_nids[1] = {
3164 0x04,
3165};
3166
3167static hda_nid_t ad1884_adc_nids[2] = {
3168 0x08, 0x09,
3169};
3170
3171static hda_nid_t ad1884_capsrc_nids[2] = {
3172 0x0c, 0x0d,
3173};
3174
3175#define AD1884_SPDIF_OUT 0x02
3176
3177static struct hda_input_mux ad1884_capture_source = {
3178 .num_items = 4,
3179 .items = {
3180 { "Front Mic", 0x0 },
3181 { "Mic", 0x1 },
3182 { "CD", 0x2 },
3183 { "Mix", 0x3 },
3184 },
3185};
3186
3187static struct snd_kcontrol_new ad1884_base_mixers[] = {
3188 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3189 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3190 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3191 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3192 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3193 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3194 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3195 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3196 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3197 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3198 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3199 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003200 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
3201 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3202 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3203 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3204 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3205 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3206 {
3207 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3208 /* The multiple "Capture Source" controls confuse alsamixer
3209 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003210 */
3211 /* .name = "Capture Source", */
3212 .name = "Input Source",
3213 .count = 2,
3214 .info = ad198x_mux_enum_info,
3215 .get = ad198x_mux_enum_get,
3216 .put = ad198x_mux_enum_put,
3217 },
3218 /* SPDIF controls */
3219 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3220 {
3221 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3222 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3223 /* identical with ad1983 */
3224 .info = ad1983_spdif_route_info,
3225 .get = ad1983_spdif_route_get,
3226 .put = ad1983_spdif_route_put,
3227 },
3228 { } /* end */
3229};
3230
3231static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3232 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3233 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3234 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003235 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003236 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003237 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003238 { } /* end */
3239};
3240
3241/*
3242 * initialization verbs
3243 */
3244static struct hda_verb ad1884_init_verbs[] = {
3245 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003246 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3247 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003248 /* Port-A (HP) mixer */
3249 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3250 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3251 /* Port-A pin */
3252 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3253 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3254 /* HP selector - select DAC2 */
3255 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3256 /* Port-D (Line-out) mixer */
3257 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3258 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3259 /* Port-D pin */
3260 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3261 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3262 /* Mono-out mixer */
3263 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3264 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3265 /* Mono-out pin */
3266 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3267 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3268 /* Mono selector */
3269 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3270 /* Port-B (front mic) pin */
3271 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003272 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003273 /* Port-C (rear mic) pin */
3274 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003275 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003276 /* Analog mixer; mute as default */
3277 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3278 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3279 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3280 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3281 /* Analog Mix output amp */
3282 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3283 /* SPDIF output selector */
3284 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3285 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3286 { } /* end */
3287};
3288
Takashi Iwaicb53c622007-08-10 17:21:45 +02003289#ifdef CONFIG_SND_HDA_POWER_SAVE
3290static struct hda_amp_list ad1884_loopbacks[] = {
3291 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3292 { 0x20, HDA_INPUT, 1 }, /* Mic */
3293 { 0x20, HDA_INPUT, 2 }, /* CD */
3294 { 0x20, HDA_INPUT, 4 }, /* Docking */
3295 { } /* end */
3296};
3297#endif
3298
Takashi Iwai2134ea42008-01-10 16:53:55 +01003299static const char *ad1884_slave_vols[] = {
3300 "PCM Playback Volume",
3301 "Mic Playback Volume",
3302 "Mono Playback Volume",
3303 "Front Mic Playback Volume",
3304 "Mic Playback Volume",
3305 "CD Playback Volume",
3306 "Internal Mic Playback Volume",
Akinobu Mitabca68462009-04-06 18:42:42 +09003307 "Docking Mic Playback Volume",
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003308 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003309 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003310 NULL
3311};
3312
Takashi Iwai2bac6472007-05-18 18:21:41 +02003313static int patch_ad1884(struct hda_codec *codec)
3314{
3315 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003316 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003317
3318 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3319 if (spec == NULL)
3320 return -ENOMEM;
3321
Takashi Iwai2bac6472007-05-18 18:21:41 +02003322 codec->spec = spec;
3323
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003324 err = snd_hda_attach_beep_device(codec, 0x10);
3325 if (err < 0) {
3326 ad198x_free(codec);
3327 return err;
3328 }
3329 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3330
Takashi Iwai2bac6472007-05-18 18:21:41 +02003331 spec->multiout.max_channels = 2;
3332 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3333 spec->multiout.dac_nids = ad1884_dac_nids;
3334 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3335 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3336 spec->adc_nids = ad1884_adc_nids;
3337 spec->capsrc_nids = ad1884_capsrc_nids;
3338 spec->input_mux = &ad1884_capture_source;
3339 spec->num_mixers = 1;
3340 spec->mixers[0] = ad1884_base_mixers;
3341 spec->num_init_verbs = 1;
3342 spec->init_verbs[0] = ad1884_init_verbs;
3343 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003344#ifdef CONFIG_SND_HDA_POWER_SAVE
3345 spec->loopback.amplist = ad1884_loopbacks;
3346#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003347 spec->vmaster_nid = 0x04;
3348 /* we need to cover all playback volumes */
3349 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003350
3351 codec->patch_ops = ad198x_patch_ops;
3352
3353 return 0;
3354}
3355
3356/*
3357 * Lenovo Thinkpad T61/X61
3358 */
3359static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003360 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003361 .items = {
3362 { "Mic", 0x0 },
3363 { "Internal Mic", 0x1 },
3364 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003365 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003366 },
3367};
3368
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003369
3370/*
3371 * Dell Precision T3400
3372 */
3373static struct hda_input_mux ad1984_dell_desktop_capture_source = {
3374 .num_items = 3,
3375 .items = {
3376 { "Front Mic", 0x0 },
3377 { "Line-In", 0x1 },
3378 { "Mix", 0x3 },
3379 },
3380};
3381
3382
Takashi Iwai2bac6472007-05-18 18:21:41 +02003383static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3384 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3385 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3386 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3387 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3388 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3389 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3390 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3391 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3392 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3393 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003394 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3395 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003396 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003397 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3398 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3399 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3400 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3401 {
3402 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3403 /* The multiple "Capture Source" controls confuse alsamixer
3404 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003405 */
3406 /* .name = "Capture Source", */
3407 .name = "Input Source",
3408 .count = 2,
3409 .info = ad198x_mux_enum_info,
3410 .get = ad198x_mux_enum_get,
3411 .put = ad198x_mux_enum_put,
3412 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003413 /* SPDIF controls */
3414 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3415 {
3416 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3417 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3418 /* identical with ad1983 */
3419 .info = ad1983_spdif_route_info,
3420 .get = ad1983_spdif_route_get,
3421 .put = ad1983_spdif_route_put,
3422 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003423 { } /* end */
3424};
3425
3426/* additional verbs */
3427static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3428 /* Port-E (docking station mic) pin */
3429 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3430 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3431 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003432 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003433 /* Analog mixer - docking mic; mute as default */
3434 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003435 /* enable EAPD bit */
3436 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003437 { } /* end */
3438};
3439
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003440/*
3441 * Dell Precision T3400
3442 */
3443static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
3444 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3445 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3446 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3447 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3448 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3449 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3450 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3451 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3452 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003453 HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
3454 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3455 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3456 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3457 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3458 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3459 {
3460 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3461 /* The multiple "Capture Source" controls confuse alsamixer
3462 * So call somewhat different..
3463 */
3464 /* .name = "Capture Source", */
3465 .name = "Input Source",
3466 .count = 2,
3467 .info = ad198x_mux_enum_info,
3468 .get = ad198x_mux_enum_get,
3469 .put = ad198x_mux_enum_put,
3470 },
3471 { } /* end */
3472};
3473
Takashi Iwai2bac6472007-05-18 18:21:41 +02003474/* Digial MIC ADC NID 0x05 + 0x06 */
3475static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3476 struct hda_codec *codec,
3477 unsigned int stream_tag,
3478 unsigned int format,
3479 struct snd_pcm_substream *substream)
3480{
3481 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3482 stream_tag, 0, format);
3483 return 0;
3484}
3485
3486static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3487 struct hda_codec *codec,
3488 struct snd_pcm_substream *substream)
3489{
Takashi Iwai888afa12008-03-18 09:57:50 +01003490 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003491 return 0;
3492}
3493
3494static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3495 .substreams = 2,
3496 .channels_min = 2,
3497 .channels_max = 2,
3498 .nid = 0x05,
3499 .ops = {
3500 .prepare = ad1984_pcm_dmic_prepare,
3501 .cleanup = ad1984_pcm_dmic_cleanup
3502 },
3503};
3504
3505static int ad1984_build_pcms(struct hda_codec *codec)
3506{
3507 struct ad198x_spec *spec = codec->spec;
3508 struct hda_pcm *info;
3509 int err;
3510
3511 err = ad198x_build_pcms(codec);
3512 if (err < 0)
3513 return err;
3514
3515 info = spec->pcm_rec + codec->num_pcms;
3516 codec->num_pcms++;
3517 info->name = "AD1984 Digital Mic";
3518 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3519 return 0;
3520}
3521
3522/* models */
3523enum {
3524 AD1984_BASIC,
3525 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003526 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003527 AD1984_MODELS
3528};
3529
3530static const char *ad1984_models[AD1984_MODELS] = {
3531 [AD1984_BASIC] = "basic",
3532 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003533 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003534};
3535
3536static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3537 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003538 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003539 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003540 {}
3541};
3542
3543static int patch_ad1984(struct hda_codec *codec)
3544{
3545 struct ad198x_spec *spec;
3546 int board_config, err;
3547
3548 err = patch_ad1884(codec);
3549 if (err < 0)
3550 return err;
3551 spec = codec->spec;
3552 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3553 ad1984_models, ad1984_cfg_tbl);
3554 switch (board_config) {
3555 case AD1984_BASIC:
3556 /* additional digital mics */
3557 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3558 codec->patch_ops.build_pcms = ad1984_build_pcms;
3559 break;
3560 case AD1984_THINKPAD:
Jerone Youngebf00c52008-01-07 12:22:18 +01003561 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003562 spec->input_mux = &ad1984_thinkpad_capture_source;
3563 spec->mixers[0] = ad1984_thinkpad_mixers;
3564 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
3565 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003566 case AD1984_DELL_DESKTOP:
3567 spec->multiout.dig_out_nid = 0;
3568 spec->input_mux = &ad1984_dell_desktop_capture_source;
3569 spec->mixers[0] = ad1984_dell_desktop_mixers;
3570 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003571 }
3572 return 0;
3573}
3574
3575
3576/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003577 * AD1883 / AD1884A / AD1984A / AD1984B
3578 *
3579 * port-B (0x14) - front mic-in
3580 * port-E (0x1c) - rear mic-in
3581 * port-F (0x16) - CD / ext out
3582 * port-C (0x15) - rear line-in
3583 * port-D (0x12) - rear line-out
3584 * port-A (0x11) - front hp-out
3585 *
3586 * AD1984A = AD1884A + digital-mic
3587 * AD1883 = equivalent with AD1984A
3588 * AD1984B = AD1984A + extra SPDIF-out
3589 *
3590 * FIXME:
3591 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3592 */
3593
3594static hda_nid_t ad1884a_dac_nids[1] = {
3595 0x03,
3596};
3597
3598#define ad1884a_adc_nids ad1884_adc_nids
3599#define ad1884a_capsrc_nids ad1884_capsrc_nids
3600
3601#define AD1884A_SPDIF_OUT 0x02
3602
3603static struct hda_input_mux ad1884a_capture_source = {
3604 .num_items = 5,
3605 .items = {
3606 { "Front Mic", 0x0 },
3607 { "Mic", 0x4 },
3608 { "Line", 0x1 },
3609 { "CD", 0x2 },
3610 { "Mix", 0x3 },
3611 },
3612};
3613
3614static struct snd_kcontrol_new ad1884a_base_mixers[] = {
3615 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3616 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3617 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3618 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3619 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3620 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3621 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3622 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3623 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3624 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3625 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3626 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3627 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3628 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3629 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3630 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003631 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3632 HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
3633 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3634 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3635 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3636 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3637 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3638 {
3639 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3640 /* The multiple "Capture Source" controls confuse alsamixer
3641 * So call somewhat different..
3642 */
3643 /* .name = "Capture Source", */
3644 .name = "Input Source",
3645 .count = 2,
3646 .info = ad198x_mux_enum_info,
3647 .get = ad198x_mux_enum_get,
3648 .put = ad198x_mux_enum_put,
3649 },
3650 /* SPDIF controls */
3651 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3652 {
3653 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3654 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3655 /* identical with ad1983 */
3656 .info = ad1983_spdif_route_info,
3657 .get = ad1983_spdif_route_get,
3658 .put = ad1983_spdif_route_put,
3659 },
3660 { } /* end */
3661};
3662
3663/*
3664 * initialization verbs
3665 */
3666static struct hda_verb ad1884a_init_verbs[] = {
3667 /* DACs; unmute as default */
3668 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3669 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3670 /* Port-A (HP) mixer - route only from analog mixer */
3671 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3672 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3673 /* Port-A pin */
3674 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3675 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3676 /* Port-D (Line-out) mixer - route only from analog mixer */
3677 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3678 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3679 /* Port-D pin */
3680 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3681 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3682 /* Mono-out mixer - route only from analog mixer */
3683 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3684 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3685 /* Mono-out pin */
3686 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3687 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3688 /* Port-B (front mic) pin */
3689 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003690 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003691 /* Port-C (rear line-in) pin */
3692 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003693 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003694 /* Port-E (rear mic) pin */
3695 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3696 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3697 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
3698 /* Port-F (CD) pin */
3699 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3700 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3701 /* Analog mixer; mute as default */
3702 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3703 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3704 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3705 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3706 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
3707 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3708 /* Analog Mix output amp */
3709 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3710 /* capture sources */
3711 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
3712 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3713 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3714 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3715 /* SPDIF output amp */
3716 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3717 { } /* end */
3718};
3719
3720#ifdef CONFIG_SND_HDA_POWER_SAVE
3721static struct hda_amp_list ad1884a_loopbacks[] = {
3722 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3723 { 0x20, HDA_INPUT, 1 }, /* Mic */
3724 { 0x20, HDA_INPUT, 2 }, /* CD */
3725 { 0x20, HDA_INPUT, 4 }, /* Docking */
3726 { } /* end */
3727};
3728#endif
3729
3730/*
3731 * Laptop model
3732 *
3733 * Port A: Headphone jack
3734 * Port B: MIC jack
3735 * Port C: Internal MIC
3736 * Port D: Dock Line Out (if enabled)
3737 * Port E: Dock Line In (if enabled)
3738 * Port F: Internal speakers
3739 */
3740
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003741static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
3742 struct snd_ctl_elem_value *ucontrol)
3743{
3744 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3745 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
3746 int mute = (!ucontrol->value.integer.value[0] &&
3747 !ucontrol->value.integer.value[1]);
3748 /* toggle GPIO1 according to the mute state */
3749 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
3750 mute ? 0x02 : 0x0);
3751 return ret;
3752}
Takashi Iwaic5059252008-02-16 09:43:56 +01003753
3754static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
3755 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003756 {
3757 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3758 .name = "Master Playback Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003759 .subdevice = HDA_SUBDEV_NID_FLAG | 0x21,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003760 .info = snd_hda_mixer_amp_switch_info,
3761 .get = snd_hda_mixer_amp_switch_get,
3762 .put = ad1884a_mobile_master_sw_put,
3763 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3764 },
Takashi Iwaic5059252008-02-16 09:43:56 +01003765 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3766 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3767 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3768 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3769 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3770 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3771 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3772 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3773 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003774 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3775 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
3776 HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3777 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3778 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003779 { } /* end */
3780};
3781
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003782static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
3783 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db17e2009-07-02 16:10:23 +02003784 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
3785 {
3786 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3787 .name = "Master Playback Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003788 .subdevice = HDA_SUBDEV_NID_FLAG | 0x21,
Takashi Iwai099db17e2009-07-02 16:10:23 +02003789 .info = snd_hda_mixer_amp_switch_info,
3790 .get = snd_hda_mixer_amp_switch_get,
3791 .put = ad1884a_mobile_master_sw_put,
3792 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3793 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003794 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3795 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02003796 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
3797 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003798 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3799 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003800 { } /* end */
3801};
3802
Takashi Iwaic5059252008-02-16 09:43:56 +01003803/* mute internal speaker if HP is plugged */
3804static void ad1884a_hp_automute(struct hda_codec *codec)
3805{
3806 unsigned int present;
3807
Takashi Iwaid56757a2009-11-18 08:00:14 +01003808 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01003809 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3810 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3811 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3812 present ? 0x00 : 0x02);
3813}
3814
Takashi Iwai269ef192008-05-30 15:32:15 +02003815/* switch to external mic if plugged */
3816static void ad1884a_hp_automic(struct hda_codec *codec)
3817{
3818 unsigned int present;
3819
Takashi Iwaid56757a2009-11-18 08:00:14 +01003820 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02003821 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
3822 present ? 0 : 1);
3823}
3824
Takashi Iwaic5059252008-02-16 09:43:56 +01003825#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02003826#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01003827
3828/* unsolicited event for HP jack sensing */
3829static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
3830{
Takashi Iwai269ef192008-05-30 15:32:15 +02003831 switch (res >> 26) {
3832 case AD1884A_HP_EVENT:
3833 ad1884a_hp_automute(codec);
3834 break;
3835 case AD1884A_MIC_EVENT:
3836 ad1884a_hp_automic(codec);
3837 break;
3838 }
Takashi Iwaic5059252008-02-16 09:43:56 +01003839}
3840
3841/* initialize jack-sensing, too */
3842static int ad1884a_hp_init(struct hda_codec *codec)
3843{
3844 ad198x_init(codec);
3845 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02003846 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01003847 return 0;
3848}
3849
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003850/* mute internal speaker if HP or docking HP is plugged */
3851static void ad1884a_laptop_automute(struct hda_codec *codec)
3852{
3853 unsigned int present;
3854
Takashi Iwaid56757a2009-11-18 08:00:14 +01003855 present = snd_hda_jack_detect(codec, 0x11);
3856 if (!present)
3857 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003858 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3859 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3860 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3861 present ? 0x00 : 0x02);
3862}
3863
3864/* switch to external mic if plugged */
3865static void ad1884a_laptop_automic(struct hda_codec *codec)
3866{
3867 unsigned int idx;
3868
Takashi Iwaid56757a2009-11-18 08:00:14 +01003869 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003870 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01003871 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003872 idx = 4;
3873 else
3874 idx = 1;
3875 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
3876}
3877
3878/* unsolicited event for HP jack sensing */
3879static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
3880 unsigned int res)
3881{
3882 switch (res >> 26) {
3883 case AD1884A_HP_EVENT:
3884 ad1884a_laptop_automute(codec);
3885 break;
3886 case AD1884A_MIC_EVENT:
3887 ad1884a_laptop_automic(codec);
3888 break;
3889 }
3890}
3891
3892/* initialize jack-sensing, too */
3893static int ad1884a_laptop_init(struct hda_codec *codec)
3894{
3895 ad198x_init(codec);
3896 ad1884a_laptop_automute(codec);
3897 ad1884a_laptop_automic(codec);
3898 return 0;
3899}
3900
Takashi Iwaic5059252008-02-16 09:43:56 +01003901/* additional verbs for laptop model */
3902static struct hda_verb ad1884a_laptop_verbs[] = {
3903 /* Port-A (HP) pin - always unmuted */
3904 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3905 /* Port-F (int speaker) mixer - route only from analog mixer */
3906 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3907 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08003908 /* Port-F (int speaker) pin */
3909 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01003910 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08003911 /* required for compaq 6530s/6531s speaker output */
3912 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003913 /* Port-C pin - internal mic-in */
3914 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3915 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3916 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02003917 /* Port-D (docking line-out) pin - default unmuted */
3918 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01003919 /* analog mix */
3920 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3921 /* unsolicited event for pin-sense */
3922 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003923 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003924 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003925 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02003926 /* allow to touch GPIO1 (for mute control) */
3927 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
3928 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
3929 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01003930 { } /* end */
3931};
3932
Takashi Iwai73156132009-04-23 08:24:48 +02003933static struct hda_verb ad1884a_mobile_verbs[] = {
3934 /* DACs; unmute as default */
3935 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3936 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3937 /* Port-A (HP) mixer - route only from analog mixer */
3938 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3939 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3940 /* Port-A pin */
3941 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3942 /* Port-A (HP) pin - always unmuted */
3943 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3944 /* Port-B (mic jack) pin */
3945 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3946 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3947 /* Port-C (int mic) pin */
3948 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3949 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3950 /* Port-F (int speaker) mixer - route only from analog mixer */
3951 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3952 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3953 /* Port-F pin */
3954 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3955 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3956 /* Analog mixer; mute as default */
3957 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3958 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3959 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3960 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3961 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3962 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3963 /* Analog Mix output amp */
3964 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3965 /* capture sources */
3966 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
3967 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3968 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3969 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3970 /* unsolicited event for pin-sense */
3971 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
3972 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db17e2009-07-02 16:10:23 +02003973 /* allow to touch GPIO1 (for mute control) */
3974 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
3975 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
3976 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02003977 { } /* end */
3978};
3979
Takashi Iwaic5059252008-02-16 09:43:56 +01003980/*
Takashi Iwaif0813742008-03-18 12:13:03 +01003981 * Thinkpad X300
3982 * 0x11 - HP
3983 * 0x12 - speaker
3984 * 0x14 - mic-in
3985 * 0x17 - built-in mic
3986 */
3987
3988static struct hda_verb ad1984a_thinkpad_verbs[] = {
3989 /* HP unmute */
3990 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3991 /* analog mix */
3992 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3993 /* turn on EAPD */
3994 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
3995 /* unsolicited event for pin-sense */
3996 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
3997 /* internal mic - dmic */
3998 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02003999 /* set magic COEFs for dmic */
4000 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4001 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01004002 { } /* end */
4003};
4004
4005static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
4006 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4007 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4008 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4009 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4010 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4011 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01004012 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
4013 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4014 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4015 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4016 {
4017 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4018 .name = "Capture Source",
4019 .info = ad198x_mux_enum_info,
4020 .get = ad198x_mux_enum_get,
4021 .put = ad198x_mux_enum_put,
4022 },
4023 { } /* end */
4024};
4025
4026static struct hda_input_mux ad1984a_thinkpad_capture_source = {
4027 .num_items = 3,
4028 .items = {
4029 { "Mic", 0x0 },
4030 { "Internal Mic", 0x5 },
4031 { "Mix", 0x3 },
4032 },
4033};
4034
4035/* mute internal speaker if HP is plugged */
4036static void ad1984a_thinkpad_automute(struct hda_codec *codec)
4037{
4038 unsigned int present;
4039
Takashi Iwaid56757a2009-11-18 08:00:14 +01004040 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01004041 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
4042 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4043}
4044
4045/* unsolicited event for HP jack sensing */
4046static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
4047 unsigned int res)
4048{
4049 if ((res >> 26) != AD1884A_HP_EVENT)
4050 return;
4051 ad1984a_thinkpad_automute(codec);
4052}
4053
4054/* initialize jack-sensing, too */
4055static int ad1984a_thinkpad_init(struct hda_codec *codec)
4056{
4057 ad198x_init(codec);
4058 ad1984a_thinkpad_automute(codec);
4059 return 0;
4060}
4061
4062/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004063 * HP Touchsmart
4064 * port-A (0x11) - front hp-out
4065 * port-B (0x14) - unused
4066 * port-C (0x15) - unused
4067 * port-D (0x12) - rear line out
4068 * port-E (0x1c) - front mic-in
4069 * port-F (0x16) - Internal speakers
4070 * digital-mic (0x17) - Internal mic
4071 */
4072
4073static struct hda_verb ad1984a_touchsmart_verbs[] = {
4074 /* DACs; unmute as default */
4075 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4076 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4077 /* Port-A (HP) mixer - route only from analog mixer */
4078 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4079 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4080 /* Port-A pin */
4081 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4082 /* Port-A (HP) pin - always unmuted */
4083 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4084 /* Port-E (int speaker) mixer - route only from analog mixer */
4085 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4086 /* Port-E pin */
4087 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4088 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4089 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4090 /* Port-F (int speaker) mixer - route only from analog mixer */
4091 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4092 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4093 /* Port-F pin */
4094 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4095 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4096 /* Analog mixer; mute as default */
4097 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4098 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4099 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4100 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4101 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4102 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4103 /* Analog Mix output amp */
4104 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4105 /* capture sources */
4106 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4107 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4108 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4109 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4110 /* unsolicited event for pin-sense */
4111 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4112 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4113 /* allow to touch GPIO1 (for mute control) */
4114 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4115 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4116 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4117 /* internal mic - dmic */
4118 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4119 /* set magic COEFs for dmic */
4120 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4121 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4122 { } /* end */
4123};
4124
4125static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
4126 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4127/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4128 {
4129 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01004130 .subdevice = HDA_SUBDEV_NID_FLAG | 0x21,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004131 .name = "Master Playback Switch",
4132 .info = snd_hda_mixer_amp_switch_info,
4133 .get = snd_hda_mixer_amp_switch_get,
4134 .put = ad1884a_mobile_master_sw_put,
4135 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4136 },
4137 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4138 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4139 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4140 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4141 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
4142 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4143 { } /* end */
4144};
4145
4146/* switch to external mic if plugged */
4147static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4148{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004149 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004150 snd_hda_codec_write(codec, 0x0c, 0,
4151 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004152 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004153 snd_hda_codec_write(codec, 0x0c, 0,
4154 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004155}
4156
4157
4158/* unsolicited event for HP jack sensing */
4159static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4160 unsigned int res)
4161{
4162 switch (res >> 26) {
4163 case AD1884A_HP_EVENT:
4164 ad1884a_hp_automute(codec);
4165 break;
4166 case AD1884A_MIC_EVENT:
4167 ad1984a_touchsmart_automic(codec);
4168 break;
4169 }
4170}
4171
4172/* initialize jack-sensing, too */
4173static int ad1984a_touchsmart_init(struct hda_codec *codec)
4174{
4175 ad198x_init(codec);
4176 ad1884a_hp_automute(codec);
4177 ad1984a_touchsmart_automic(codec);
4178 return 0;
4179}
4180
4181
4182/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004183 */
4184
4185enum {
4186 AD1884A_DESKTOP,
4187 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004188 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004189 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004190 AD1984A_TOUCHSMART,
Takashi Iwaic5059252008-02-16 09:43:56 +01004191 AD1884A_MODELS
4192};
4193
4194static const char *ad1884a_models[AD1884A_MODELS] = {
4195 [AD1884A_DESKTOP] = "desktop",
4196 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004197 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004198 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004199 [AD1984A_TOUCHSMART] = "touchsmart",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004200};
4201
4202static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
4203 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004204 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004205 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004206 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004207 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004208 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4209 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004210 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004211 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004212 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004213 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004214};
4215
4216static int patch_ad1884a(struct hda_codec *codec)
4217{
4218 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004219 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004220
4221 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4222 if (spec == NULL)
4223 return -ENOMEM;
4224
Takashi Iwaic5059252008-02-16 09:43:56 +01004225 codec->spec = spec;
4226
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004227 err = snd_hda_attach_beep_device(codec, 0x10);
4228 if (err < 0) {
4229 ad198x_free(codec);
4230 return err;
4231 }
4232 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4233
Takashi Iwaic5059252008-02-16 09:43:56 +01004234 spec->multiout.max_channels = 2;
4235 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4236 spec->multiout.dac_nids = ad1884a_dac_nids;
4237 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4238 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4239 spec->adc_nids = ad1884a_adc_nids;
4240 spec->capsrc_nids = ad1884a_capsrc_nids;
4241 spec->input_mux = &ad1884a_capture_source;
4242 spec->num_mixers = 1;
4243 spec->mixers[0] = ad1884a_base_mixers;
4244 spec->num_init_verbs = 1;
4245 spec->init_verbs[0] = ad1884a_init_verbs;
4246 spec->spdif_route = 0;
4247#ifdef CONFIG_SND_HDA_POWER_SAVE
4248 spec->loopback.amplist = ad1884a_loopbacks;
4249#endif
4250 codec->patch_ops = ad198x_patch_ops;
4251
4252 /* override some parameters */
4253 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004254 ad1884a_models,
4255 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01004256 switch (board_config) {
4257 case AD1884A_LAPTOP:
4258 spec->mixers[0] = ad1884a_laptop_mixers;
4259 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4260 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004261 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4262 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004263 /* set the upper-limit for mixer amp to 0dB for avoiding the
4264 * possible damage by overloading
4265 */
4266 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4267 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4268 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4269 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4270 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004271 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004272 case AD1884A_MOBILE:
4273 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004274 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004275 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004276 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4277 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004278 /* set the upper-limit for mixer amp to 0dB for avoiding the
4279 * possible damage by overloading
4280 */
4281 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4282 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4283 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4284 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4285 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004286 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004287 case AD1884A_THINKPAD:
4288 spec->mixers[0] = ad1984a_thinkpad_mixers;
4289 spec->init_verbs[spec->num_init_verbs++] =
4290 ad1984a_thinkpad_verbs;
4291 spec->multiout.dig_out_nid = 0;
4292 spec->input_mux = &ad1984a_thinkpad_capture_source;
4293 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4294 codec->patch_ops.init = ad1984a_thinkpad_init;
4295 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004296 case AD1984A_TOUCHSMART:
4297 spec->mixers[0] = ad1984a_touchsmart_mixers;
4298 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4299 spec->multiout.dig_out_nid = 0;
4300 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4301 codec->patch_ops.init = ad1984a_touchsmart_init;
4302 /* set the upper-limit for mixer amp to 0dB for avoiding the
4303 * possible damage by overloading
4304 */
4305 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4306 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4307 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4308 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4309 (1 << AC_AMPCAP_MUTE_SHIFT));
4310 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004311 }
4312
4313 return 0;
4314}
4315
4316
4317/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004318 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004319 *
4320 * port-A - front hp-out
4321 * port-B - front mic-in
4322 * port-C - rear line-in, shared surr-out (3stack)
4323 * port-D - rear line-out
4324 * port-E - rear mic-in, shared clfe-out (3stack)
4325 * port-F - rear surr-out (6stack)
4326 * port-G - rear clfe-out (6stack)
4327 */
4328
4329static hda_nid_t ad1882_dac_nids[3] = {
4330 0x04, 0x03, 0x05
4331};
4332
4333static hda_nid_t ad1882_adc_nids[2] = {
4334 0x08, 0x09,
4335};
4336
4337static hda_nid_t ad1882_capsrc_nids[2] = {
4338 0x0c, 0x0d,
4339};
4340
4341#define AD1882_SPDIF_OUT 0x02
4342
4343/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
4344static struct hda_input_mux ad1882_capture_source = {
4345 .num_items = 5,
4346 .items = {
4347 { "Front Mic", 0x1 },
4348 { "Mic", 0x4 },
4349 { "Line", 0x2 },
4350 { "CD", 0x3 },
4351 { "Mix", 0x7 },
4352 },
4353};
4354
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004355/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
4356static struct hda_input_mux ad1882a_capture_source = {
4357 .num_items = 5,
4358 .items = {
4359 { "Front Mic", 0x1 },
4360 { "Mic", 0x4},
4361 { "Line", 0x2 },
4362 { "Digital Mic", 0x06 },
4363 { "Mix", 0x7 },
4364 },
4365};
4366
Takashi Iwai0ac85512007-06-20 15:46:13 +02004367static struct snd_kcontrol_new ad1882_base_mixers[] = {
4368 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4369 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4370 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4371 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4372 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4373 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4374 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4375 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004376
Takashi Iwai0ac85512007-06-20 15:46:13 +02004377 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
4378 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
4379 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
4380 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4381 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4382 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4383 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4384 {
4385 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4386 /* The multiple "Capture Source" controls confuse alsamixer
4387 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004388 */
4389 /* .name = "Capture Source", */
4390 .name = "Input Source",
4391 .count = 2,
4392 .info = ad198x_mux_enum_info,
4393 .get = ad198x_mux_enum_get,
4394 .put = ad198x_mux_enum_put,
4395 },
4396 /* SPDIF controls */
4397 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4398 {
4399 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4400 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4401 /* identical with ad1983 */
4402 .info = ad1983_spdif_route_info,
4403 .get = ad1983_spdif_route_get,
4404 .put = ad1983_spdif_route_put,
4405 },
4406 { } /* end */
4407};
4408
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004409static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
4410 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4411 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4412 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4413 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4414 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4415 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4416 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4417 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004418 { } /* end */
4419};
4420
4421static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
4422 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4423 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4424 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4425 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4426 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4427 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4428 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4429 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004430 HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
4431 { } /* end */
4432};
4433
Takashi Iwai0ac85512007-06-20 15:46:13 +02004434static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
4435 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4436 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4437 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4438 {
4439 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4440 .name = "Channel Mode",
4441 .info = ad198x_ch_mode_info,
4442 .get = ad198x_ch_mode_get,
4443 .put = ad198x_ch_mode_put,
4444 },
4445 { } /* end */
4446};
4447
4448static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
4449 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4450 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4451 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4452 { } /* end */
4453};
4454
4455static struct hda_verb ad1882_ch2_init[] = {
4456 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4457 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4458 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4459 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4460 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4461 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4462 { } /* end */
4463};
4464
4465static struct hda_verb ad1882_ch4_init[] = {
4466 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4467 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4468 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4469 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4470 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4471 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4472 { } /* end */
4473};
4474
4475static struct hda_verb ad1882_ch6_init[] = {
4476 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4477 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4478 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4479 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4480 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4481 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4482 { } /* end */
4483};
4484
4485static struct hda_channel_mode ad1882_modes[3] = {
4486 { 2, ad1882_ch2_init },
4487 { 4, ad1882_ch4_init },
4488 { 6, ad1882_ch6_init },
4489};
4490
4491/*
4492 * initialization verbs
4493 */
4494static struct hda_verb ad1882_init_verbs[] = {
4495 /* DACs; mute as default */
4496 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4497 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4498 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4499 /* Port-A (HP) mixer */
4500 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4501 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4502 /* Port-A pin */
4503 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4504 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4505 /* HP selector - select DAC2 */
4506 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4507 /* Port-D (Line-out) mixer */
4508 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4509 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4510 /* Port-D pin */
4511 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4512 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4513 /* Mono-out mixer */
4514 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4515 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4516 /* Mono-out pin */
4517 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4518 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4519 /* Port-B (front mic) pin */
4520 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4521 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4522 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4523 /* Port-C (line-in) pin */
4524 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4525 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4526 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4527 /* Port-C mixer - mute as input */
4528 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4529 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4530 /* Port-E (mic-in) pin */
4531 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4532 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4533 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4534 /* Port-E mixer - mute as input */
4535 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4536 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4537 /* Port-F (surround) */
4538 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4539 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4540 /* Port-G (CLFE) */
4541 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4542 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4543 /* Analog mixer; mute as default */
4544 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4545 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4546 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4547 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4548 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4549 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4550 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4551 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4552 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4553 /* Analog Mix output amp */
4554 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4555 /* SPDIF output selector */
4556 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4557 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4558 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4559 { } /* end */
4560};
4561
Takashi Iwaicb53c622007-08-10 17:21:45 +02004562#ifdef CONFIG_SND_HDA_POWER_SAVE
4563static struct hda_amp_list ad1882_loopbacks[] = {
4564 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4565 { 0x20, HDA_INPUT, 1 }, /* Mic */
4566 { 0x20, HDA_INPUT, 4 }, /* Line */
4567 { 0x20, HDA_INPUT, 6 }, /* CD */
4568 { } /* end */
4569};
4570#endif
4571
Takashi Iwai0ac85512007-06-20 15:46:13 +02004572/* models */
4573enum {
4574 AD1882_3STACK,
4575 AD1882_6STACK,
4576 AD1882_MODELS
4577};
4578
4579static const char *ad1882_models[AD1986A_MODELS] = {
4580 [AD1882_3STACK] = "3stack",
4581 [AD1882_6STACK] = "6stack",
4582};
4583
4584
4585static int patch_ad1882(struct hda_codec *codec)
4586{
4587 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004588 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004589
4590 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4591 if (spec == NULL)
4592 return -ENOMEM;
4593
Takashi Iwai0ac85512007-06-20 15:46:13 +02004594 codec->spec = spec;
4595
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004596 err = snd_hda_attach_beep_device(codec, 0x10);
4597 if (err < 0) {
4598 ad198x_free(codec);
4599 return err;
4600 }
4601 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4602
Takashi Iwai0ac85512007-06-20 15:46:13 +02004603 spec->multiout.max_channels = 6;
4604 spec->multiout.num_dacs = 3;
4605 spec->multiout.dac_nids = ad1882_dac_nids;
4606 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
4607 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
4608 spec->adc_nids = ad1882_adc_nids;
4609 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004610 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004611 spec->input_mux = &ad1882_capture_source;
4612 else
4613 spec->input_mux = &ad1882a_capture_source;
4614 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004615 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004616 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004617 spec->mixers[1] = ad1882_loopback_mixers;
4618 else
4619 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004620 spec->num_init_verbs = 1;
4621 spec->init_verbs[0] = ad1882_init_verbs;
4622 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02004623#ifdef CONFIG_SND_HDA_POWER_SAVE
4624 spec->loopback.amplist = ad1882_loopbacks;
4625#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01004626 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004627
4628 codec->patch_ops = ad198x_patch_ops;
4629
4630 /* override some parameters */
4631 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
4632 ad1882_models, NULL);
4633 switch (board_config) {
4634 default:
4635 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004636 spec->num_mixers = 3;
4637 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004638 spec->channel_mode = ad1882_modes;
4639 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
4640 spec->need_dac_fix = 1;
4641 spec->multiout.max_channels = 2;
4642 spec->multiout.num_dacs = 1;
4643 break;
4644 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004645 spec->num_mixers = 3;
4646 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004647 break;
4648 }
4649 return 0;
4650}
4651
4652
4653/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004654 * patch entries
4655 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004656static struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004657 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02004658 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004659 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004660 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004661 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
4662 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02004663 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
4664 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004665 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004666 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01004667 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02004668 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004669 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02004670 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
4671 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004672 {} /* terminator */
4673};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004674
4675MODULE_ALIAS("snd-hda-codec-id:11d4*");
4676
4677MODULE_LICENSE("GPL");
4678MODULE_DESCRIPTION("Analog Devices HD-audio codec");
4679
4680static struct hda_codec_preset_list analog_list = {
4681 .preset = snd_hda_preset_analog,
4682 .owner = THIS_MODULE,
4683};
4684
4685static int __init patch_analog_init(void)
4686{
4687 return snd_hda_add_codec_preset(&analog_list);
4688}
4689
4690static void __exit patch_analog_exit(void)
4691{
4692 snd_hda_delete_codec_preset(&analog_list);
4693}
4694
4695module_init(patch_analog_init)
4696module_exit(patch_analog_exit)