blob: 262f5b5decf1dadd9db811fa1fa36b36afa767c0 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Takashi Iwai0ac85512007-06-20 15:46:13 +02002 * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
3 * AD1986A, AD1988
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
Takashi Iwai2bac6472007-05-18 18:21:41 +02005 * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 *
7 * This driver is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This driver is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/init.h>
23#include <linux/delay.h>
24#include <linux/slab.h>
25#include <linux/pci.h>
Ingo Molnar62932df2006-01-16 16:34:20 +010026
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <sound/core.h>
28#include "hda_codec.h"
29#include "hda_local.h"
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010030#include "hda_beep.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020032struct ad198x_spec {
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +010033 struct snd_kcontrol_new *mixers[5];
Takashi Iwai985be542005-11-02 18:26:49 +010034 int num_mixers;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010035 unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
Takashi Iwaid32410b12005-11-24 16:06:23 +010036 const struct hda_verb *init_verbs[5]; /* initialization verbs
Takashi Iwai985be542005-11-02 18:26:49 +010037 * don't forget NULL termination!
38 */
39 unsigned int num_init_verbs;
40
41 /* playback */
42 struct hda_multi_out multiout; /* playback set-up
43 * max_channels, dacs must be set
44 * dig_out_nid and hp_nid are optional
45 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +010046 unsigned int cur_eapd;
Takashi Iwai2125cad2006-03-27 12:52:22 +020047 unsigned int need_dac_fix;
Takashi Iwai985be542005-11-02 18:26:49 +010048
49 /* capture */
50 unsigned int num_adc_nids;
51 hda_nid_t *adc_nids;
52 hda_nid_t dig_in_nid; /* digital-in NID; optional */
53
54 /* capture source */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020055 const struct hda_input_mux *input_mux;
Takashi Iwai2e5b9562005-11-21 16:36:15 +010056 hda_nid_t *capsrc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010057 unsigned int cur_mux[3];
58
59 /* channel model */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +010060 const struct hda_channel_mode *channel_mode;
Takashi Iwai985be542005-11-02 18:26:49 +010061 int num_channel_mode;
62
63 /* PCM information */
Takashi Iwai2bac6472007-05-18 18:21:41 +020064 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
Takashi Iwai985be542005-11-02 18:26:49 +010065
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020066 unsigned int spdif_route;
Takashi Iwaid32410b12005-11-24 16:06:23 +010067
68 /* dynamic controls, init_verbs and input_mux */
69 struct auto_pin_cfg autocfg;
Takashi Iwai603c4012008-07-30 15:01:44 +020070 struct snd_array kctls;
Takashi Iwaid32410b12005-11-24 16:06:23 +010071 struct hda_input_mux private_imux;
Takashi Iwai41923e42007-10-22 17:20:10 +020072 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
Takashi Iwaicb53c622007-08-10 17:21:45 +020073
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +010074 unsigned int jack_present: 1;
75 unsigned int inv_jack_detect: 1;/* inverted jack-detection */
76 unsigned int inv_eapd: 1; /* inverted EAPD implementation */
77 unsigned int analog_beep: 1; /* analog beep input present */
Takashi Iwai8ab78c72007-09-06 14:29:53 +020078
Takashi Iwaicb53c622007-08-10 17:21:45 +020079#ifdef CONFIG_SND_HDA_POWER_SAVE
80 struct hda_loopback_check loopback;
81#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +010082 /* for virtual master */
83 hda_nid_t vmaster_nid;
Takashi Iwai2134ea42008-01-10 16:53:55 +010084 const char **slave_vols;
85 const char **slave_sws;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086};
87
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020088/*
89 * input MUX handling (common part)
90 */
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +010091static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020092{
93 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
94 struct ad198x_spec *spec = codec->spec;
95
96 return snd_hda_input_mux_info(spec->input_mux, uinfo);
97}
98
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +010099static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200100{
101 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
102 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100103 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200104
Takashi Iwai985be542005-11-02 18:26:49 +0100105 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200106 return 0;
107}
108
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100109static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200110{
111 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
112 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100113 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200114
115 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai2e5b9562005-11-21 16:36:15 +0100116 spec->capsrc_nids[adc_idx],
117 &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200118}
119
120/*
121 * initialization (common callbacks)
122 */
123static int ad198x_init(struct hda_codec *codec)
124{
125 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100126 int i;
127
128 for (i = 0; i < spec->num_init_verbs; i++)
129 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200130 return 0;
131}
132
Takashi Iwai2134ea42008-01-10 16:53:55 +0100133static const char *ad_slave_vols[] = {
134 "Front Playback Volume",
135 "Surround Playback Volume",
136 "Center Playback Volume",
137 "LFE Playback Volume",
138 "Side Playback Volume",
139 "Headphone Playback Volume",
140 "Mono Playback Volume",
Takashi Iwai628ed132008-01-25 11:56:57 +0100141 "Speaker Playback Volume",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100142 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100143 NULL
144};
145
146static const char *ad_slave_sws[] = {
147 "Front Playback Switch",
148 "Surround Playback Switch",
149 "Center Playback Switch",
150 "LFE Playback Switch",
151 "Side Playback Switch",
152 "Headphone Playback Switch",
153 "Mono Playback Switch",
Takashi Iwai628ed132008-01-25 11:56:57 +0100154 "Speaker Playback Switch",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100155 "IEC958 Playback Switch",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100156 NULL
157};
158
Takashi Iwai603c4012008-07-30 15:01:44 +0200159static void ad198x_free_kctls(struct hda_codec *codec);
160
Takashi Iwai67d634c2009-11-16 15:35:59 +0100161#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100162/* additional beep mixers; the actual parameters are overwritten at build */
163static struct snd_kcontrol_new ad_beep_mixer[] = {
164 HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
Jaroslav Kysela123c07a2009-10-21 14:48:23 +0200165 HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100166 { } /* end */
167};
168
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +0100169static struct snd_kcontrol_new ad_beep2_mixer[] = {
170 HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
171 HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
172 { } /* end */
173};
174
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100175#define set_beep_amp(spec, nid, idx, dir) \
176 ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100177#else
178#define set_beep_amp(spec, nid, idx, dir) /* NOP */
179#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100180
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200181static int ad198x_build_controls(struct hda_codec *codec)
182{
183 struct ad198x_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100184 struct snd_kcontrol *kctl;
Takashi Iwai985be542005-11-02 18:26:49 +0100185 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200186 int err;
187
Takashi Iwai985be542005-11-02 18:26:49 +0100188 for (i = 0; i < spec->num_mixers; i++) {
189 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
190 if (err < 0)
191 return err;
192 }
193 if (spec->multiout.dig_out_nid) {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200194 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100195 if (err < 0)
196 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100197 err = snd_hda_create_spdif_share_sw(codec,
198 &spec->multiout);
199 if (err < 0)
200 return err;
201 spec->multiout.share_spdif = 1;
Takashi Iwai985be542005-11-02 18:26:49 +0100202 }
203 if (spec->dig_in_nid) {
204 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
205 if (err < 0)
206 return err;
207 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100208
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100209 /* create beep controls if needed */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100210#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100211 if (spec->beep_amp) {
212 struct snd_kcontrol_new *knew;
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +0100213 knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
214 for ( ; knew->name; knew++) {
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100215 struct snd_kcontrol *kctl;
216 kctl = snd_ctl_new1(knew, codec);
217 if (!kctl)
218 return -ENOMEM;
219 kctl->private_value = spec->beep_amp;
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100220 err = snd_hda_ctl_add(codec, 0, kctl);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100221 if (err < 0)
222 return err;
223 }
224 }
Takashi Iwai67d634c2009-11-16 15:35:59 +0100225#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100226
Takashi Iwai2134ea42008-01-10 16:53:55 +0100227 /* if we have no master control, let's create it */
228 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100229 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100230 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100231 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100232 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100233 vmaster_tlv,
Takashi Iwai2134ea42008-01-10 16:53:55 +0100234 (spec->slave_vols ?
235 spec->slave_vols : ad_slave_vols));
236 if (err < 0)
237 return err;
238 }
239 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
240 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
241 NULL,
242 (spec->slave_sws ?
243 spec->slave_sws : ad_slave_sws));
244 if (err < 0)
245 return err;
246 }
247
Takashi Iwai603c4012008-07-30 15:01:44 +0200248 ad198x_free_kctls(codec); /* no longer needed */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100249
250 /* assign Capture Source enums to NID */
251 kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
252 if (!kctl)
253 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
254 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +0100255 err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100256 if (err < 0)
257 return err;
258 }
259
260 /* assign IEC958 enums to NID */
261 kctl = snd_hda_find_mixer_ctl(codec,
262 SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
263 if (kctl) {
264 err = snd_hda_add_nid(codec, kctl, 0,
265 spec->multiout.dig_out_nid);
266 if (err < 0)
267 return err;
268 }
269
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200270 return 0;
271}
272
Takashi Iwaicb53c622007-08-10 17:21:45 +0200273#ifdef CONFIG_SND_HDA_POWER_SAVE
274static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
275{
276 struct ad198x_spec *spec = codec->spec;
277 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
278}
279#endif
280
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200281/*
282 * Analog playback callbacks
283 */
284static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
285 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100286 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200287{
288 struct ad198x_spec *spec = codec->spec;
Takashi Iwai9a081602008-02-12 18:37:26 +0100289 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
290 hinfo);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200291}
292
293static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
294 struct hda_codec *codec,
295 unsigned int stream_tag,
296 unsigned int format,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100297 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200298{
299 struct ad198x_spec *spec = codec->spec;
300 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
301 format, substream);
302}
303
304static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
305 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100306 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200307{
308 struct ad198x_spec *spec = codec->spec;
309 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
310}
311
312/*
313 * Digital out
314 */
315static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
316 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100317 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200318{
319 struct ad198x_spec *spec = codec->spec;
320 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
321}
322
323static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
324 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100325 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200326{
327 struct ad198x_spec *spec = codec->spec;
328 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
329}
330
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200331static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
332 struct hda_codec *codec,
333 unsigned int stream_tag,
334 unsigned int format,
335 struct snd_pcm_substream *substream)
336{
337 struct ad198x_spec *spec = codec->spec;
338 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
339 format, substream);
340}
341
Takashi Iwai9411e212009-02-13 11:32:28 +0100342static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
343 struct hda_codec *codec,
344 struct snd_pcm_substream *substream)
345{
346 struct ad198x_spec *spec = codec->spec;
347 return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
348}
349
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200350/*
351 * Analog capture
352 */
353static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
354 struct hda_codec *codec,
355 unsigned int stream_tag,
356 unsigned int format,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100357 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200358{
359 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100360 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
361 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200362 return 0;
363}
364
365static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
366 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100367 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200368{
369 struct ad198x_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +0100370 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200371 return 0;
372}
373
374
375/*
376 */
377static struct hda_pcm_stream ad198x_pcm_analog_playback = {
378 .substreams = 1,
379 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100380 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200381 .nid = 0, /* fill later */
382 .ops = {
383 .open = ad198x_playback_pcm_open,
384 .prepare = ad198x_playback_pcm_prepare,
385 .cleanup = ad198x_playback_pcm_cleanup
386 },
387};
388
389static struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100390 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200391 .channels_min = 2,
392 .channels_max = 2,
393 .nid = 0, /* fill later */
394 .ops = {
395 .prepare = ad198x_capture_pcm_prepare,
396 .cleanup = ad198x_capture_pcm_cleanup
397 },
398};
399
400static struct hda_pcm_stream ad198x_pcm_digital_playback = {
401 .substreams = 1,
402 .channels_min = 2,
403 .channels_max = 2,
404 .nid = 0, /* fill later */
405 .ops = {
406 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200407 .close = ad198x_dig_playback_pcm_close,
Takashi Iwai9411e212009-02-13 11:32:28 +0100408 .prepare = ad198x_dig_playback_pcm_prepare,
409 .cleanup = ad198x_dig_playback_pcm_cleanup
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200410 },
411};
412
Takashi Iwai985be542005-11-02 18:26:49 +0100413static struct hda_pcm_stream ad198x_pcm_digital_capture = {
414 .substreams = 1,
415 .channels_min = 2,
416 .channels_max = 2,
417 /* NID is set in alc_build_pcms */
418};
419
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200420static int ad198x_build_pcms(struct hda_codec *codec)
421{
422 struct ad198x_spec *spec = codec->spec;
423 struct hda_pcm *info = spec->pcm_rec;
424
425 codec->num_pcms = 1;
426 codec->pcm_info = info;
427
428 info->name = "AD198x Analog";
429 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
430 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
431 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
432 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100433 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
434 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200435
436 if (spec->multiout.dig_out_nid) {
437 info++;
438 codec->num_pcms++;
439 info->name = "AD198x Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +0100440 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200441 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
442 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100443 if (spec->dig_in_nid) {
444 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
445 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
446 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200447 }
448
449 return 0;
450}
451
Daniel T Chenea52bf22009-12-27 18:48:29 -0500452static inline void ad198x_shutup(struct hda_codec *codec)
453{
454 snd_hda_shutup_pins(codec);
455}
456
Takashi Iwai603c4012008-07-30 15:01:44 +0200457static void ad198x_free_kctls(struct hda_codec *codec)
458{
459 struct ad198x_spec *spec = codec->spec;
460
461 if (spec->kctls.list) {
462 struct snd_kcontrol_new *kctl = spec->kctls.list;
463 int i;
464 for (i = 0; i < spec->kctls.used; i++)
465 kfree(kctl[i].name);
466 }
467 snd_array_free(&spec->kctls);
468}
469
Daniel T Chenea52bf22009-12-27 18:48:29 -0500470static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
471 hda_nid_t hp)
472{
473 struct ad198x_spec *spec = codec->spec;
474 snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
475 !spec->inv_eapd ? 0x00 : 0x02);
476 snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
477 !spec->inv_eapd ? 0x00 : 0x02);
478}
479
480static void ad198x_power_eapd(struct hda_codec *codec)
481{
482 /* We currently only handle front, HP */
483 switch (codec->vendor_id) {
484 case 0x11d41882:
485 case 0x11d4882a:
486 case 0x11d41884:
487 case 0x11d41984:
488 case 0x11d41883:
489 case 0x11d4184a:
490 case 0x11d4194a:
491 case 0x11d4194b:
492 ad198x_power_eapd_write(codec, 0x12, 0x11);
493 break;
494 case 0x11d41981:
495 case 0x11d41983:
496 ad198x_power_eapd_write(codec, 0x05, 0x06);
497 break;
498 case 0x11d41986:
499 ad198x_power_eapd_write(codec, 0x1b, 0x1a);
500 break;
501 case 0x11d41988:
502 case 0x11d4198b:
503 case 0x11d4989a:
504 case 0x11d4989b:
505 ad198x_power_eapd_write(codec, 0x29, 0x22);
506 break;
507 }
508}
509
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200510static void ad198x_free(struct hda_codec *codec)
511{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100512 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +0100513
Takashi Iwai603c4012008-07-30 15:01:44 +0200514 if (!spec)
515 return;
516
Daniel T Chenea52bf22009-12-27 18:48:29 -0500517 ad198x_shutup(codec);
Takashi Iwai603c4012008-07-30 15:01:44 +0200518 ad198x_free_kctls(codec);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100519 kfree(spec);
520 snd_hda_detach_beep_device(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200521}
522
Daniel T Chenea52bf22009-12-27 18:48:29 -0500523#ifdef SND_HDA_NEEDS_RESUME
524static int ad198x_suspend(struct hda_codec *codec, pm_message_t state)
525{
526 ad198x_shutup(codec);
527 ad198x_power_eapd(codec);
528 return 0;
529}
530
531static int ad198x_resume(struct hda_codec *codec)
532{
533 ad198x_init(codec);
534 snd_hda_codec_resume_amp(codec);
535 snd_hda_codec_resume_cache(codec);
536 return 0;
537}
538#endif
539
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200540static struct hda_codec_ops ad198x_patch_ops = {
541 .build_controls = ad198x_build_controls,
542 .build_pcms = ad198x_build_pcms,
543 .init = ad198x_init,
544 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200545#ifdef CONFIG_SND_HDA_POWER_SAVE
546 .check_power_status = ad198x_check_power_status,
547#endif
Daniel T Chenea52bf22009-12-27 18:48:29 -0500548#ifdef SND_HDA_NEEDS_RESUME
549 .suspend = ad198x_suspend,
550 .resume = ad198x_resume,
551#endif
552 .reboot_notify = ad198x_shutup,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200553};
554
555
556/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100557 * EAPD control
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100558 * the private value = nid
Takashi Iwai18a815d2006-03-01 19:54:39 +0100559 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200560#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100561
562static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
563 struct snd_ctl_elem_value *ucontrol)
564{
565 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
566 struct ad198x_spec *spec = codec->spec;
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100567 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100568 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
569 else
570 ucontrol->value.integer.value[0] = spec->cur_eapd;
571 return 0;
572}
573
574static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
575 struct snd_ctl_elem_value *ucontrol)
576{
577 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
578 struct ad198x_spec *spec = codec->spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +0100579 hda_nid_t nid = kcontrol->private_value & 0xff;
580 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100581 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100582 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100583 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200584 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100585 return 0;
586 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200587 snd_hda_codec_write_cache(codec, nid,
588 0, AC_VERB_SET_EAPD_BTLENABLE,
589 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100590 return 1;
591}
592
Takashi Iwai9230d212006-03-13 13:49:49 +0100593static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
594 struct snd_ctl_elem_info *uinfo);
595static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
596 struct snd_ctl_elem_value *ucontrol);
597static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
598 struct snd_ctl_elem_value *ucontrol);
599
600
Takashi Iwai18a815d2006-03-01 19:54:39 +0100601/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200602 * AD1986A specific
603 */
604
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605#define AD1986A_SPDIF_OUT 0x02
606#define AD1986A_FRONT_DAC 0x03
607#define AD1986A_SURR_DAC 0x04
608#define AD1986A_CLFE_DAC 0x05
609#define AD1986A_ADC 0x06
610
611static hda_nid_t ad1986a_dac_nids[3] = {
612 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
613};
Takashi Iwai985be542005-11-02 18:26:49 +0100614static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +0100615static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616
617static struct hda_input_mux ad1986a_capture_source = {
618 .num_items = 7,
619 .items = {
620 { "Mic", 0x0 },
621 { "CD", 0x1 },
622 { "Aux", 0x3 },
623 { "Line", 0x4 },
624 { "Mix", 0x5 },
625 { "Mono", 0x6 },
626 { "Phone", 0x7 },
627 },
628};
629
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630
Takashi Iwai532d5382007-07-27 19:02:40 +0200631static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
632 .ops = &snd_hda_bind_vol,
633 .values = {
634 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
635 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
636 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
637 0
638 },
639};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640
Takashi Iwai532d5382007-07-27 19:02:40 +0200641static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
642 .ops = &snd_hda_bind_sw,
643 .values = {
644 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
645 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
646 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
647 0
648 },
649};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
651/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 * mixers
653 */
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100654static struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200655 /*
656 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
657 */
658 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
659 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
661 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
662 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
663 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
664 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
665 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
666 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
667 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
668 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
669 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
670 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
671 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
672 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
673 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
674 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
675 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
676 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
677 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100678 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
680 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
681 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
682 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
683 {
684 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
685 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200686 .info = ad198x_mux_enum_info,
687 .get = ad198x_mux_enum_get,
688 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 },
690 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
691 { } /* end */
692};
693
Takashi Iwai9230d212006-03-13 13:49:49 +0100694/* additional mixers for 3stack mode */
695static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
696 {
697 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
698 .name = "Channel Mode",
699 .info = ad198x_ch_mode_info,
700 .get = ad198x_ch_mode_get,
701 .put = ad198x_ch_mode_put,
702 },
703 { } /* end */
704};
705
706/* laptop model - 2ch only */
707static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
708
Takashi Iwai20a45e82007-08-15 22:20:45 +0200709/* master controls both pins 0x1a and 0x1b */
710static struct hda_bind_ctls ad1986a_laptop_master_vol = {
711 .ops = &snd_hda_bind_vol,
712 .values = {
713 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
714 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
715 0,
716 },
717};
718
719static struct hda_bind_ctls ad1986a_laptop_master_sw = {
720 .ops = &snd_hda_bind_sw,
721 .values = {
722 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
723 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
724 0,
725 },
726};
727
Takashi Iwai9230d212006-03-13 13:49:49 +0100728static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
729 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
730 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200731 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
732 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100733 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
734 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
735 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
736 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
737 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
738 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
739 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
740 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100741 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100742 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100743 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
744 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
745 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
746 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
747 {
748 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
749 .name = "Capture Source",
750 .info = ad198x_mux_enum_info,
751 .get = ad198x_mux_enum_get,
752 .put = ad198x_mux_enum_put,
753 },
754 { } /* end */
755};
756
Takashi Iwai825aa972006-03-17 10:50:49 +0100757/* laptop-eapd model - 2ch only */
758
Takashi Iwai825aa972006-03-17 10:50:49 +0100759static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
760 .num_items = 3,
761 .items = {
762 { "Mic", 0x0 },
763 { "Internal Mic", 0x4 },
764 { "Mix", 0x5 },
765 },
766};
767
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100768static struct hda_input_mux ad1986a_automic_capture_source = {
769 .num_items = 2,
770 .items = {
771 { "Mic", 0x0 },
772 { "Mix", 0x5 },
773 },
774};
775
Takashi Iwai16d11a82009-06-24 14:07:53 +0200776static struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200777 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
778 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai16d11a82009-06-24 14:07:53 +0200779 { } /* end */
780};
781
782static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai825aa972006-03-17 10:50:49 +0100783 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
784 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100785 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
786 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
787 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
788 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
789 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
790 {
791 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
792 .name = "Capture Source",
793 .info = ad198x_mux_enum_info,
794 .get = ad198x_mux_enum_get,
795 .put = ad198x_mux_enum_put,
796 },
797 {
798 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
799 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100800 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwai1725b822008-11-21 02:25:48 +0100801 .info = ad198x_eapd_info,
802 .get = ad198x_eapd_get,
803 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100804 .private_value = 0x1b, /* port-D */
Takashi Iwai1725b822008-11-21 02:25:48 +0100805 },
806 { } /* end */
807};
808
Takashi Iwai16d11a82009-06-24 14:07:53 +0200809static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
810 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
811 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
Takashi Iwai825aa972006-03-17 10:50:49 +0100812 { } /* end */
813};
814
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100815/* re-connect the mic boost input according to the jack sensing */
816static void ad1986a_automic(struct hda_codec *codec)
817{
818 unsigned int present;
Takashi Iwaid56757a2009-11-18 08:00:14 +0100819 present = snd_hda_jack_detect(codec, 0x1f);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100820 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
821 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
Takashi Iwaid56757a2009-11-18 08:00:14 +0100822 present ? 0 : 2);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100823}
824
825#define AD1986A_MIC_EVENT 0x36
826
827static void ad1986a_automic_unsol_event(struct hda_codec *codec,
828 unsigned int res)
829{
830 if ((res >> 26) != AD1986A_MIC_EVENT)
831 return;
832 ad1986a_automic(codec);
833}
834
835static int ad1986a_automic_init(struct hda_codec *codec)
836{
837 ad198x_init(codec);
838 ad1986a_automic(codec);
839 return 0;
840}
841
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200842/* laptop-automute - 2ch only */
843
844static void ad1986a_update_hp(struct hda_codec *codec)
845{
846 struct ad198x_spec *spec = codec->spec;
847 unsigned int mute;
848
849 if (spec->jack_present)
850 mute = HDA_AMP_MUTE; /* mute internal speaker */
851 else
852 /* unmute internal speaker if necessary */
853 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
854 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
855 HDA_AMP_MUTE, mute);
856}
857
858static void ad1986a_hp_automute(struct hda_codec *codec)
859{
860 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200861
Takashi Iwaid56757a2009-11-18 08:00:14 +0100862 spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
Takashi Iwai03c405a2009-06-24 14:10:15 +0200863 if (spec->inv_jack_detect)
864 spec->jack_present = !spec->jack_present;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200865 ad1986a_update_hp(codec);
866}
867
868#define AD1986A_HP_EVENT 0x37
869
870static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
871{
872 if ((res >> 26) != AD1986A_HP_EVENT)
873 return;
874 ad1986a_hp_automute(codec);
875}
876
877static int ad1986a_hp_init(struct hda_codec *codec)
878{
879 ad198x_init(codec);
880 ad1986a_hp_automute(codec);
881 return 0;
882}
883
884/* bind hp and internal speaker mute (with plug check) */
885static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
886 struct snd_ctl_elem_value *ucontrol)
887{
888 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
889 long *valp = ucontrol->value.integer.value;
890 int change;
891
892 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
893 HDA_AMP_MUTE,
894 valp[0] ? 0 : HDA_AMP_MUTE);
895 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
896 HDA_AMP_MUTE,
897 valp[1] ? 0 : HDA_AMP_MUTE);
898 if (change)
899 ad1986a_update_hp(codec);
900 return change;
901}
902
Takashi Iwai16d11a82009-06-24 14:07:53 +0200903static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200904 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
905 {
906 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
907 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100908 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200909 .info = snd_hda_mixer_amp_switch_info,
910 .get = snd_hda_mixer_amp_switch_get,
911 .put = ad1986a_hp_master_sw_put,
912 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
913 },
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200914 { } /* end */
915};
916
Takashi Iwai16d11a82009-06-24 14:07:53 +0200917
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918/*
919 * initialization verbs
920 */
921static struct hda_verb ad1986a_init_verbs[] = {
922 /* Front, Surround, CLFE DAC; mute as default */
923 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
924 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
925 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
926 /* Downmix - off */
927 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
928 /* HP, Line-Out, Surround, CLFE selectors */
929 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
930 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
931 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
932 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
933 /* Mono selector */
934 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
935 /* Mic selector: Mic 1/2 pin */
936 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
937 /* Line-in selector: Line-in */
938 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
939 /* Mic 1/2 swap */
940 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
941 /* Record selector: mic */
942 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
943 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
944 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
945 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
946 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
947 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
948 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
949 /* PC beep */
950 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
951 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
952 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
953 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
954 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
955 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
956 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200957 /* HP Pin */
958 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
959 /* Front, Surround, CLFE Pins */
960 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
961 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
962 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
963 /* Mono Pin */
964 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
965 /* Mic Pin */
966 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
967 /* Line, Aux, CD, Beep-In Pin */
968 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
969 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
970 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
971 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
972 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 { } /* end */
974};
975
Takashi Iwai9230d212006-03-13 13:49:49 +0100976static struct hda_verb ad1986a_ch2_init[] = {
977 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200978 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
979 /* Line-in selectors */
980 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100981 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200982 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
983 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
984 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100985 { } /* end */
986};
987
988static struct hda_verb ad1986a_ch4_init[] = {
989 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200990 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
991 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100992 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200993 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
994 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100995 { } /* end */
996};
997
998static struct hda_verb ad1986a_ch6_init[] = {
999 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001000 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1001 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001002 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001003 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1004 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001005 { } /* end */
1006};
1007
1008static struct hda_channel_mode ad1986a_modes[3] = {
1009 { 2, ad1986a_ch2_init },
1010 { 4, ad1986a_ch4_init },
1011 { 6, ad1986a_ch6_init },
1012};
1013
Takashi Iwai825aa972006-03-17 10:50:49 +01001014/* eapd initialization */
1015static struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001016 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa972006-03-17 10:50:49 +01001017 {}
1018};
1019
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001020static struct hda_verb ad1986a_automic_verbs[] = {
1021 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1022 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1023 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
1024 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
1025 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
1026 {}
1027};
1028
Tobin Davisf36090f2007-01-08 11:07:12 +01001029/* Ultra initialization */
1030static struct hda_verb ad1986a_ultra_init[] = {
1031 /* eapd initialization */
1032 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
1033 /* CLFE -> Mic in */
1034 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
1035 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1036 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
1037 { } /* end */
1038};
1039
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001040/* pin sensing on HP jack */
1041static struct hda_verb ad1986a_hp_init_verbs[] = {
1042 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
1043 {}
1044};
1045
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001046static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
1047 unsigned int res)
1048{
1049 switch (res >> 26) {
1050 case AD1986A_HP_EVENT:
1051 ad1986a_hp_automute(codec);
1052 break;
1053 case AD1986A_MIC_EVENT:
1054 ad1986a_automic(codec);
1055 break;
1056 }
1057}
1058
1059static int ad1986a_samsung_p50_init(struct hda_codec *codec)
1060{
1061 ad198x_init(codec);
1062 ad1986a_hp_automute(codec);
1063 ad1986a_automic(codec);
1064 return 0;
1065}
1066
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001067
Takashi Iwai9230d212006-03-13 13:49:49 +01001068/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001069enum {
1070 AD1986A_6STACK,
1071 AD1986A_3STACK,
1072 AD1986A_LAPTOP,
1073 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001074 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +01001075 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +01001076 AD1986A_SAMSUNG,
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001077 AD1986A_SAMSUNG_P50,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001078 AD1986A_MODELS
1079};
Takashi Iwai9230d212006-03-13 13:49:49 +01001080
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001081static const char *ad1986a_models[AD1986A_MODELS] = {
1082 [AD1986A_6STACK] = "6stack",
1083 [AD1986A_3STACK] = "3stack",
1084 [AD1986A_LAPTOP] = "laptop",
1085 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001086 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +01001087 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +01001088 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001089 [AD1986A_SAMSUNG_P50] = "samsung-p50",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001090};
1091
1092static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
1093 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001094 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001095 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001096 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001097 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1098 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1099 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1100 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001101 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001102 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001103 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1104 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1105 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1106 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1107 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001108 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Daniel T Chenba579eb2010-02-20 11:16:30 -05001109 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001110 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001111 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001112 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
Tobin Davisf36090f2007-01-08 11:07:12 +01001113 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001114 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001115 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001116 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001117 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001118 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001119 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001120 {}
1121};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122
Takashi Iwaicb53c622007-08-10 17:21:45 +02001123#ifdef CONFIG_SND_HDA_POWER_SAVE
1124static struct hda_amp_list ad1986a_loopbacks[] = {
1125 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1126 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1127 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1128 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1129 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1130 { } /* end */
1131};
1132#endif
1133
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001134static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1135{
Takashi Iwai2f334f92009-02-20 14:37:42 +01001136 unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001137 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1138}
1139
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140static int patch_ad1986a(struct hda_codec *codec)
1141{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001142 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001143 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001145 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 if (spec == NULL)
1147 return -ENOMEM;
1148
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 codec->spec = spec;
1150
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001151 err = snd_hda_attach_beep_device(codec, 0x19);
1152 if (err < 0) {
1153 ad198x_free(codec);
1154 return err;
1155 }
1156 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1157
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 spec->multiout.max_channels = 6;
1159 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1160 spec->multiout.dac_nids = ad1986a_dac_nids;
1161 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001162 spec->num_adc_nids = 1;
1163 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001164 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001165 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001166 spec->num_mixers = 1;
1167 spec->mixers[0] = ad1986a_mixers;
1168 spec->num_init_verbs = 1;
1169 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001170#ifdef CONFIG_SND_HDA_POWER_SAVE
1171 spec->loopback.amplist = ad1986a_loopbacks;
1172#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001173 spec->vmaster_nid = 0x1b;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01001174 spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001176 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177
Takashi Iwai9230d212006-03-13 13:49:49 +01001178 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001179 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1180 ad1986a_models,
1181 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001182 switch (board_config) {
1183 case AD1986A_3STACK:
1184 spec->num_mixers = 2;
1185 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001186 spec->num_init_verbs = 2;
1187 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001188 spec->channel_mode = ad1986a_modes;
1189 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001190 spec->need_dac_fix = 1;
1191 spec->multiout.max_channels = 2;
1192 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001193 break;
1194 case AD1986A_LAPTOP:
1195 spec->mixers[0] = ad1986a_laptop_mixers;
1196 spec->multiout.max_channels = 2;
1197 spec->multiout.num_dacs = 1;
1198 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1199 break;
Takashi Iwai825aa972006-03-17 10:50:49 +01001200 case AD1986A_LAPTOP_EAPD:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001201 spec->num_mixers = 3;
1202 spec->mixers[0] = ad1986a_laptop_master_mixers;
1203 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1204 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001205 spec->num_init_verbs = 2;
1206 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1207 spec->multiout.max_channels = 2;
1208 spec->multiout.num_dacs = 1;
1209 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1210 if (!is_jack_available(codec, 0x25))
1211 spec->multiout.dig_out_nid = 0;
1212 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1213 break;
1214 case AD1986A_SAMSUNG:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001215 spec->num_mixers = 2;
1216 spec->mixers[0] = ad1986a_laptop_master_mixers;
1217 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001218 spec->num_init_verbs = 3;
Takashi Iwai825aa972006-03-17 10:50:49 +01001219 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001220 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa972006-03-17 10:50:49 +01001221 spec->multiout.max_channels = 2;
1222 spec->multiout.num_dacs = 1;
1223 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001224 if (!is_jack_available(codec, 0x25))
1225 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001226 spec->input_mux = &ad1986a_automic_capture_source;
1227 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1228 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa972006-03-17 10:50:49 +01001229 break;
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001230 case AD1986A_SAMSUNG_P50:
1231 spec->num_mixers = 2;
1232 spec->mixers[0] = ad1986a_automute_master_mixers;
1233 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1234 spec->num_init_verbs = 4;
1235 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1236 spec->init_verbs[2] = ad1986a_automic_verbs;
1237 spec->init_verbs[3] = ad1986a_hp_init_verbs;
1238 spec->multiout.max_channels = 2;
1239 spec->multiout.num_dacs = 1;
1240 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1241 if (!is_jack_available(codec, 0x25))
1242 spec->multiout.dig_out_nid = 0;
1243 spec->input_mux = &ad1986a_automic_capture_source;
1244 codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
1245 codec->patch_ops.init = ad1986a_samsung_p50_init;
1246 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001247 case AD1986A_LAPTOP_AUTOMUTE:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001248 spec->num_mixers = 3;
1249 spec->mixers[0] = ad1986a_automute_master_mixers;
1250 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1251 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001252 spec->num_init_verbs = 3;
1253 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1254 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1255 spec->multiout.max_channels = 2;
1256 spec->multiout.num_dacs = 1;
1257 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001258 if (!is_jack_available(codec, 0x25))
1259 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001260 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1261 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1262 codec->patch_ops.init = ad1986a_hp_init;
Takashi Iwai03c405a2009-06-24 14:10:15 +02001263 /* Lenovo N100 seems to report the reversed bit
1264 * for HP jack-sensing
1265 */
1266 spec->inv_jack_detect = 1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001267 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001268 case AD1986A_ULTRA:
1269 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1270 spec->num_init_verbs = 2;
1271 spec->init_verbs[1] = ad1986a_ultra_init;
1272 spec->multiout.max_channels = 2;
1273 spec->multiout.num_dacs = 1;
1274 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1275 spec->multiout.dig_out_nid = 0;
1276 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001277 }
1278
Takashi Iwaid29240c2007-10-26 12:35:56 +02001279 /* AD1986A has a hardware problem that it can't share a stream
1280 * with multiple output pins. The copy of front to surrounds
1281 * causes noisy or silent outputs at a certain timing, e.g.
1282 * changing the volume.
1283 * So, let's disable the shared stream.
1284 */
1285 spec->multiout.no_share_stream = 1;
1286
Takashi Iwai729d55b2009-12-25 22:49:01 +01001287 codec->no_trigger_sense = 1;
1288
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 return 0;
1290}
1291
1292/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001293 * AD1983 specific
1294 */
1295
1296#define AD1983_SPDIF_OUT 0x02
1297#define AD1983_DAC 0x03
1298#define AD1983_ADC 0x04
1299
1300static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001301static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001302static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001303
1304static struct hda_input_mux ad1983_capture_source = {
1305 .num_items = 4,
1306 .items = {
1307 { "Mic", 0x0 },
1308 { "Line", 0x1 },
1309 { "Mix", 0x2 },
1310 { "Mix Mono", 0x3 },
1311 },
1312};
1313
1314/*
1315 * SPDIF playback route
1316 */
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001317static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001318{
1319 static char *texts[] = { "PCM", "ADC" };
1320
1321 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1322 uinfo->count = 1;
1323 uinfo->value.enumerated.items = 2;
1324 if (uinfo->value.enumerated.item > 1)
1325 uinfo->value.enumerated.item = 1;
1326 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1327 return 0;
1328}
1329
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001330static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001331{
1332 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1333 struct ad198x_spec *spec = codec->spec;
1334
1335 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1336 return 0;
1337}
1338
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001339static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001340{
1341 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1342 struct ad198x_spec *spec = codec->spec;
1343
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001344 if (ucontrol->value.enumerated.item[0] > 1)
1345 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001346 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1347 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001348 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1349 AC_VERB_SET_CONNECT_SEL,
1350 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001351 return 1;
1352 }
1353 return 0;
1354}
1355
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001356static struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001357 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1358 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1359 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1360 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1361 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1362 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1363 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1364 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1365 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1366 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1367 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1368 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001369 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
1370 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1371 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1372 {
1373 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1374 .name = "Capture Source",
1375 .info = ad198x_mux_enum_info,
1376 .get = ad198x_mux_enum_get,
1377 .put = ad198x_mux_enum_put,
1378 },
1379 {
1380 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001381 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001382 .info = ad1983_spdif_route_info,
1383 .get = ad1983_spdif_route_get,
1384 .put = ad1983_spdif_route_put,
1385 },
1386 { } /* end */
1387};
1388
1389static struct hda_verb ad1983_init_verbs[] = {
1390 /* Front, HP, Mono; mute as default */
1391 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1392 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1393 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1394 /* Beep, PCM, Mic, Line-In: mute */
1395 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1396 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1397 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1398 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1399 /* Front, HP selectors; from Mix */
1400 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1401 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1402 /* Mono selector; from Mix */
1403 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1404 /* Mic selector; Mic */
1405 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1406 /* Line-in selector: Line-in */
1407 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1408 /* Mic boost: 0dB */
1409 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1410 /* Record selector: mic */
1411 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1412 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1413 /* SPDIF route: PCM */
1414 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1415 /* Front Pin */
1416 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1417 /* HP Pin */
1418 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1419 /* Mono Pin */
1420 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1421 /* Mic Pin */
1422 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1423 /* Line Pin */
1424 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1425 { } /* end */
1426};
1427
Takashi Iwaicb53c622007-08-10 17:21:45 +02001428#ifdef CONFIG_SND_HDA_POWER_SAVE
1429static struct hda_amp_list ad1983_loopbacks[] = {
1430 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1431 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1432 { } /* end */
1433};
1434#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001435
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001436static int patch_ad1983(struct hda_codec *codec)
1437{
1438 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001439 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001440
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001441 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001442 if (spec == NULL)
1443 return -ENOMEM;
1444
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001445 codec->spec = spec;
1446
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001447 err = snd_hda_attach_beep_device(codec, 0x10);
1448 if (err < 0) {
1449 ad198x_free(codec);
1450 return err;
1451 }
1452 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1453
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001454 spec->multiout.max_channels = 2;
1455 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1456 spec->multiout.dac_nids = ad1983_dac_nids;
1457 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001458 spec->num_adc_nids = 1;
1459 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001460 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001461 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001462 spec->num_mixers = 1;
1463 spec->mixers[0] = ad1983_mixers;
1464 spec->num_init_verbs = 1;
1465 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001466 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001467#ifdef CONFIG_SND_HDA_POWER_SAVE
1468 spec->loopback.amplist = ad1983_loopbacks;
1469#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001470 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001471
1472 codec->patch_ops = ad198x_patch_ops;
1473
Takashi Iwai729d55b2009-12-25 22:49:01 +01001474 codec->no_trigger_sense = 1;
1475
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001476 return 0;
1477}
1478
1479
1480/*
1481 * AD1981 HD specific
1482 */
1483
1484#define AD1981_SPDIF_OUT 0x02
1485#define AD1981_DAC 0x03
1486#define AD1981_ADC 0x04
1487
1488static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001489static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001490static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001491
1492/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
1493static struct hda_input_mux ad1981_capture_source = {
1494 .num_items = 7,
1495 .items = {
1496 { "Front Mic", 0x0 },
1497 { "Line", 0x1 },
1498 { "Mix", 0x2 },
1499 { "Mix Mono", 0x3 },
1500 { "CD", 0x4 },
1501 { "Mic", 0x6 },
1502 { "Aux", 0x7 },
1503 },
1504};
1505
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001506static struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001507 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1508 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1509 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1510 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1511 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1512 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1513 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1514 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1515 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1516 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1517 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1518 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1519 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1520 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1521 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1522 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1523 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1524 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001525 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
1526 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
1527 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1528 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1529 {
1530 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1531 .name = "Capture Source",
1532 .info = ad198x_mux_enum_info,
1533 .get = ad198x_mux_enum_get,
1534 .put = ad198x_mux_enum_put,
1535 },
1536 /* identical with AD1983 */
1537 {
1538 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001539 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001540 .info = ad1983_spdif_route_info,
1541 .get = ad1983_spdif_route_get,
1542 .put = ad1983_spdif_route_put,
1543 },
1544 { } /* end */
1545};
1546
1547static struct hda_verb ad1981_init_verbs[] = {
1548 /* Front, HP, Mono; mute as default */
1549 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1550 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1551 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1552 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1553 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1554 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1555 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1556 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1557 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1558 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1559 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1560 /* Front, HP selectors; from Mix */
1561 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1562 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1563 /* Mono selector; from Mix */
1564 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1565 /* Mic Mixer; select Front Mic */
1566 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1567 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1568 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001569 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1570 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001571 /* Record selector: Front mic */
1572 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1573 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1574 /* SPDIF route: PCM */
1575 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1576 /* Front Pin */
1577 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1578 /* HP Pin */
1579 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1580 /* Mono Pin */
1581 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1582 /* Front & Rear Mic Pins */
1583 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1584 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1585 /* Line Pin */
1586 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1587 /* Digital Beep */
1588 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1589 /* Line-Out as Input: disabled */
1590 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1591 { } /* end */
1592};
1593
Takashi Iwaicb53c622007-08-10 17:21:45 +02001594#ifdef CONFIG_SND_HDA_POWER_SAVE
1595static struct hda_amp_list ad1981_loopbacks[] = {
1596 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1597 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1598 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1599 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1600 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1601 { } /* end */
1602};
1603#endif
1604
Takashi Iwai18a815d2006-03-01 19:54:39 +01001605/*
1606 * Patch for HP nx6320
1607 *
Tobin Davis18768992007-03-12 22:20:51 +01001608 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001609 * speaker output enabled _and_ mute-LED off.
1610 */
1611
1612#define AD1981_HP_EVENT 0x37
1613#define AD1981_MIC_EVENT 0x38
1614
1615static struct hda_verb ad1981_hp_init_verbs[] = {
1616 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1617 /* pin sensing on HP and Mic jacks */
1618 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1619 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1620 {}
1621};
1622
1623/* turn on/off EAPD (+ mute HP) as a master switch */
1624static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1625 struct snd_ctl_elem_value *ucontrol)
1626{
1627 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1628 struct ad198x_spec *spec = codec->spec;
1629
1630 if (! ad198x_eapd_put(kcontrol, ucontrol))
1631 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001632 /* change speaker pin appropriately */
1633 snd_hda_codec_write(codec, 0x05, 0,
1634 AC_VERB_SET_PIN_WIDGET_CONTROL,
1635 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001636 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001637 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1638 HDA_AMP_MUTE,
1639 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001640 return 1;
1641}
1642
1643/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02001644static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
1645 .ops = &snd_hda_bind_vol,
1646 .values = {
1647 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1648 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1649 0
1650 },
1651};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001652
1653/* mute internal speaker if HP is plugged */
1654static void ad1981_hp_automute(struct hda_codec *codec)
1655{
1656 unsigned int present;
1657
Takashi Iwaid56757a2009-11-18 08:00:14 +01001658 present = snd_hda_jack_detect(codec, 0x06);
Takashi Iwai47fd8302007-08-10 17:11:07 +02001659 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1660 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001661}
1662
1663/* toggle input of built-in and mic jack appropriately */
1664static void ad1981_hp_automic(struct hda_codec *codec)
1665{
1666 static struct hda_verb mic_jack_on[] = {
1667 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1668 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1669 {}
1670 };
1671 static struct hda_verb mic_jack_off[] = {
1672 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1673 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1674 {}
1675 };
1676 unsigned int present;
1677
Takashi Iwaid56757a2009-11-18 08:00:14 +01001678 present = snd_hda_jack_detect(codec, 0x08);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001679 if (present)
1680 snd_hda_sequence_write(codec, mic_jack_on);
1681 else
1682 snd_hda_sequence_write(codec, mic_jack_off);
1683}
1684
1685/* unsolicited event for HP jack sensing */
1686static void ad1981_hp_unsol_event(struct hda_codec *codec,
1687 unsigned int res)
1688{
1689 res >>= 26;
1690 switch (res) {
1691 case AD1981_HP_EVENT:
1692 ad1981_hp_automute(codec);
1693 break;
1694 case AD1981_MIC_EVENT:
1695 ad1981_hp_automic(codec);
1696 break;
1697 }
1698}
1699
1700static struct hda_input_mux ad1981_hp_capture_source = {
1701 .num_items = 3,
1702 .items = {
1703 { "Mic", 0x0 },
1704 { "Docking-Station", 0x1 },
1705 { "Mix", 0x2 },
1706 },
1707};
1708
1709static struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001710 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001711 {
1712 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001713 .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
Takashi Iwai18a815d2006-03-01 19:54:39 +01001714 .name = "Master Playback Switch",
1715 .info = ad198x_eapd_info,
1716 .get = ad198x_eapd_get,
1717 .put = ad1981_hp_master_sw_put,
1718 .private_value = 0x05,
1719 },
1720 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1721 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1722#if 0
1723 /* FIXME: analog mic/line loopback doesn't work with my tests...
1724 * (although recording is OK)
1725 */
1726 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1727 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1728 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1729 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1730 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1731 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1732 /* FIXME: does this laptop have analog CD connection? */
1733 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1734 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1735#endif
1736 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1737 HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
1738 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1739 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1740 {
1741 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1742 .name = "Capture Source",
1743 .info = ad198x_mux_enum_info,
1744 .get = ad198x_mux_enum_get,
1745 .put = ad198x_mux_enum_put,
1746 },
1747 { } /* end */
1748};
1749
1750/* initialize jack-sensing, too */
1751static int ad1981_hp_init(struct hda_codec *codec)
1752{
1753 ad198x_init(codec);
1754 ad1981_hp_automute(codec);
1755 ad1981_hp_automic(codec);
1756 return 0;
1757}
1758
Tobin Davis18768992007-03-12 22:20:51 +01001759/* configuration for Toshiba Laptops */
1760static struct hda_verb ad1981_toshiba_init_verbs[] = {
1761 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1762 /* pin sensing on HP and Mic jacks */
1763 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1764 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1765 {}
1766};
1767
1768static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
1769 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1770 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1771 { }
1772};
1773
Takashi Iwai01686c52006-04-18 12:54:11 +02001774/* configuration for Lenovo Thinkpad T60 */
1775static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
1776 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1777 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1778 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1779 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1780 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1781 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1782 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1783 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1784 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1785 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1786 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1787 {
1788 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1789 .name = "Capture Source",
1790 .info = ad198x_mux_enum_info,
1791 .get = ad198x_mux_enum_get,
1792 .put = ad198x_mux_enum_put,
1793 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001794 /* identical with AD1983 */
1795 {
1796 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1797 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1798 .info = ad1983_spdif_route_info,
1799 .get = ad1983_spdif_route_get,
1800 .put = ad1983_spdif_route_put,
1801 },
Takashi Iwai01686c52006-04-18 12:54:11 +02001802 { } /* end */
1803};
1804
1805static struct hda_input_mux ad1981_thinkpad_capture_source = {
1806 .num_items = 3,
1807 .items = {
1808 { "Mic", 0x0 },
1809 { "Mix", 0x2 },
1810 { "CD", 0x4 },
1811 },
1812};
1813
Takashi Iwai18a815d2006-03-01 19:54:39 +01001814/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001815enum {
1816 AD1981_BASIC,
1817 AD1981_HP,
1818 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001819 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001820 AD1981_MODELS
1821};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001822
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001823static const char *ad1981_models[AD1981_MODELS] = {
1824 [AD1981_HP] = "hp",
1825 [AD1981_THINKPAD] = "thinkpad",
1826 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001827 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001828};
1829
1830static struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001831 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001832 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001833 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001834 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001835 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c52006-04-18 12:54:11 +02001836 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001837 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001838 /* HP nx6320 (reversed SSID, H/W bug) */
1839 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001840 {}
1841};
1842
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001843static int patch_ad1981(struct hda_codec *codec)
1844{
1845 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001846 int err, board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001847
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001848 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001849 if (spec == NULL)
1850 return -ENOMEM;
1851
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001852 codec->spec = spec;
1853
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001854 err = snd_hda_attach_beep_device(codec, 0x10);
1855 if (err < 0) {
1856 ad198x_free(codec);
1857 return err;
1858 }
1859 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
1860
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001861 spec->multiout.max_channels = 2;
1862 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1863 spec->multiout.dac_nids = ad1981_dac_nids;
1864 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001865 spec->num_adc_nids = 1;
1866 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001867 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001868 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001869 spec->num_mixers = 1;
1870 spec->mixers[0] = ad1981_mixers;
1871 spec->num_init_verbs = 1;
1872 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001873 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001874#ifdef CONFIG_SND_HDA_POWER_SAVE
1875 spec->loopback.amplist = ad1981_loopbacks;
1876#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001877 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001878
1879 codec->patch_ops = ad198x_patch_ops;
1880
Takashi Iwai18a815d2006-03-01 19:54:39 +01001881 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001882 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1883 ad1981_models,
1884 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001885 switch (board_config) {
1886 case AD1981_HP:
1887 spec->mixers[0] = ad1981_hp_mixers;
1888 spec->num_init_verbs = 2;
1889 spec->init_verbs[1] = ad1981_hp_init_verbs;
1890 spec->multiout.dig_out_nid = 0;
1891 spec->input_mux = &ad1981_hp_capture_source;
1892
1893 codec->patch_ops.init = ad1981_hp_init;
1894 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
Daniel T Chen01f59662009-12-13 16:22:58 -05001895 /* set the upper-limit for mixer amp to 0dB for avoiding the
1896 * possible damage by overloading
1897 */
1898 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
1899 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
1900 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
1901 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
1902 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai18a815d2006-03-01 19:54:39 +01001903 break;
Takashi Iwai01686c52006-04-18 12:54:11 +02001904 case AD1981_THINKPAD:
1905 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c52006-04-18 12:54:11 +02001906 spec->input_mux = &ad1981_thinkpad_capture_source;
1907 break;
Tobin Davis18768992007-03-12 22:20:51 +01001908 case AD1981_TOSHIBA:
1909 spec->mixers[0] = ad1981_hp_mixers;
1910 spec->mixers[1] = ad1981_toshiba_mixers;
1911 spec->num_init_verbs = 2;
1912 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1913 spec->multiout.dig_out_nid = 0;
1914 spec->input_mux = &ad1981_hp_capture_source;
1915 codec->patch_ops.init = ad1981_hp_init;
1916 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1917 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001918 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01001919
1920 codec->no_trigger_sense = 1;
1921
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001922 return 0;
1923}
1924
1925
1926/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001927 * AD1988
1928 *
1929 * Output pins and routes
1930 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001931 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001932 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1933 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1934 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1935 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1936 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1937 * port-F 0x16 (mute) <- 0x2a <- 06
1938 * port-G 0x24 (mute) <- 0x27 <- 05
1939 * port-H 0x25 (mute) <- 0x28 <- 0a
1940 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1941 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001942 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1943 * (*) 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 +01001944 *
1945 * Input pins and routes
1946 *
1947 * pin boost mix input # / adc input #
1948 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1949 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1950 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1951 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1952 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1953 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1954 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1955 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1956 *
1957 *
1958 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001959 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001960 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001961 *
1962 * Inputs of Analog Mix (0x20)
1963 * 0:Port-B (front mic)
1964 * 1:Port-C/G/H (line-in)
1965 * 2:Port-A
1966 * 3:Port-D (line-in/2)
1967 * 4:Port-E/G/H (mic-in)
1968 * 5:Port-F (mic2-in)
1969 * 6:CD
1970 * 7:Beep
1971 *
1972 * ADC selection
1973 * 0:Port-A
1974 * 1:Port-B (front mic-in)
1975 * 2:Port-C (line-in)
1976 * 3:Port-F (mic2-in)
1977 * 4:Port-E (mic-in)
1978 * 5:CD
1979 * 6:Port-G
1980 * 7:Port-H
1981 * 8:Port-D (line-in/2)
1982 * 9:Mix
1983 *
1984 * Proposed pin assignments by the datasheet
1985 *
1986 * 6-stack
1987 * Port-A front headphone
1988 * B front mic-in
1989 * C rear line-in
1990 * D rear front-out
1991 * E rear mic-in
1992 * F rear surround
1993 * G rear CLFE
1994 * H rear side
1995 *
1996 * 3-stack
1997 * Port-A front headphone
1998 * B front mic
1999 * C rear line-in/surround
2000 * D rear front-out
2001 * E rear mic-in/CLFE
2002 *
2003 * laptop
2004 * Port-A headphone
2005 * B mic-in
2006 * C docking station
2007 * D internal speaker (with EAPD)
2008 * E/F quad mic array
2009 */
2010
2011
2012/* models */
2013enum {
2014 AD1988_6STACK,
2015 AD1988_6STACK_DIG,
2016 AD1988_3STACK,
2017 AD1988_3STACK_DIG,
2018 AD1988_LAPTOP,
2019 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01002020 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002021 AD1988_MODEL_LAST,
2022};
2023
Takashi Iwaid32410b12005-11-24 16:06:23 +01002024/* reivision id to check workarounds */
2025#define AD1988A_REV2 0x100200
2026
Takashi Iwai1a806f42006-07-03 15:58:16 +02002027#define is_rev2(codec) \
2028 ((codec)->vendor_id == 0x11d41988 && \
2029 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002030
2031/*
2032 * mixers
2033 */
2034
Takashi Iwaid32410b12005-11-24 16:06:23 +01002035static hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002036 0x04, 0x06, 0x05, 0x0a
2037};
2038
Takashi Iwaid32410b12005-11-24 16:06:23 +01002039static hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002040 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002041};
2042
2043/* for AD1988A revision-2, DAC2-4 are swapped */
2044static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
2045 0x04, 0x05, 0x0a, 0x06
2046};
2047
2048static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002049 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002050};
2051
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002052static hda_nid_t ad1988_adc_nids[3] = {
2053 0x08, 0x09, 0x0f
2054};
2055
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002056static hda_nid_t ad1988_capsrc_nids[3] = {
2057 0x0c, 0x0d, 0x0e
2058};
2059
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002060#define AD1988_SPDIF_OUT 0x02
2061#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002062#define AD1988_SPDIF_IN 0x07
2063
Takashi Iwai3a08e302009-02-13 11:37:08 +01002064static hda_nid_t ad1989b_slave_dig_outs[] = {
2065 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002066};
2067
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002068static struct hda_input_mux ad1988_6stack_capture_source = {
2069 .num_items = 5,
2070 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002071 { "Front Mic", 0x1 }, /* port-B */
2072 { "Line", 0x2 }, /* port-C */
2073 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002074 { "CD", 0x5 },
2075 { "Mix", 0x9 },
2076 },
2077};
2078
2079static struct hda_input_mux ad1988_laptop_capture_source = {
2080 .num_items = 3,
2081 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002082 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002083 { "CD", 0x5 },
2084 { "Mix", 0x9 },
2085 },
2086};
2087
2088/*
2089 */
2090static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
2091 struct snd_ctl_elem_info *uinfo)
2092{
2093 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2094 struct ad198x_spec *spec = codec->spec;
2095 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
2096 spec->num_channel_mode);
2097}
2098
2099static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
2100 struct snd_ctl_elem_value *ucontrol)
2101{
2102 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2103 struct ad198x_spec *spec = codec->spec;
2104 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
2105 spec->num_channel_mode, spec->multiout.max_channels);
2106}
2107
2108static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
2109 struct snd_ctl_elem_value *ucontrol)
2110{
2111 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2112 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002113 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
2114 spec->num_channel_mode,
2115 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02002116 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02002117 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002118 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002119}
2120
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002121/* 6-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002122static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002123 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2124 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
2125 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2126 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
2127 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002128 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002129};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002130
Takashi Iwaid32410b12005-11-24 16:06:23 +01002131static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
2132 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2133 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2134 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2135 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2136 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002137 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002138};
2139
2140static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002141 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2142 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2143 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2144 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2145 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2146 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2147 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2148
2149 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2150 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2151 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2152 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2153 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2154 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2155 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2156 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2157
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002158 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002159 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2160
2161 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2162 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2163
2164 { } /* end */
2165};
2166
2167/* 3-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002168static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002169 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002170 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002171 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2172 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002173 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002174};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002175
Takashi Iwaid32410b12005-11-24 16:06:23 +01002176static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
2177 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002178 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2179 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2180 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002181 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002182};
2183
2184static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002185 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002186 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2187 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2188 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002189 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2190 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2191
2192 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2193 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2194 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2195 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2196 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2197 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2198 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2199 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2200
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002201 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002202 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2203
2204 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2205 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2206 {
2207 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2208 .name = "Channel Mode",
2209 .info = ad198x_ch_mode_info,
2210 .get = ad198x_ch_mode_get,
2211 .put = ad198x_ch_mode_put,
2212 },
2213
2214 { } /* end */
2215};
2216
2217/* laptop mode */
2218static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
2219 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2220 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2221 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2222
2223 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2224 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2225 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2226 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2227 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2228 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2229
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002230 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002231 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2232
2233 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2234
2235 {
2236 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2237 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002238 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01002239 .info = ad198x_eapd_info,
2240 .get = ad198x_eapd_get,
2241 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01002242 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002243 },
2244
2245 { } /* end */
2246};
2247
2248/* capture */
2249static struct snd_kcontrol_new ad1988_capture_mixers[] = {
2250 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2251 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2252 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2253 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2254 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2255 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2256 {
2257 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2258 /* The multiple "Capture Source" controls confuse alsamixer
2259 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002260 */
2261 /* .name = "Capture Source", */
2262 .name = "Input Source",
2263 .count = 3,
2264 .info = ad198x_mux_enum_info,
2265 .get = ad198x_mux_enum_get,
2266 .put = ad198x_mux_enum_put,
2267 },
2268 { } /* end */
2269};
2270
2271static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2272 struct snd_ctl_elem_info *uinfo)
2273{
2274 static char *texts[] = {
2275 "PCM", "ADC1", "ADC2", "ADC3"
2276 };
2277 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2278 uinfo->count = 1;
2279 uinfo->value.enumerated.items = 4;
2280 if (uinfo->value.enumerated.item >= 4)
2281 uinfo->value.enumerated.item = 3;
2282 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2283 return 0;
2284}
2285
2286static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2287 struct snd_ctl_elem_value *ucontrol)
2288{
2289 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2290 unsigned int sel;
2291
Takashi Iwaibddcf542007-07-24 18:04:05 +02002292 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2293 AC_AMP_GET_INPUT);
2294 if (!(sel & 0x80))
2295 ucontrol->value.enumerated.item[0] = 0;
2296 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002297 sel = snd_hda_codec_read(codec, 0x0b, 0,
2298 AC_VERB_GET_CONNECT_SEL, 0);
2299 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002300 sel++;
2301 else
2302 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002303 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002304 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002305 return 0;
2306}
2307
2308static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2309 struct snd_ctl_elem_value *ucontrol)
2310{
2311 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002312 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002313 int change;
2314
Takashi Iwai35b26722007-05-05 12:17:17 +02002315 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002316 if (val > 3)
2317 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002318 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002319 sel = snd_hda_codec_read(codec, 0x1d, 0,
2320 AC_VERB_GET_AMP_GAIN_MUTE,
2321 AC_AMP_GET_INPUT);
2322 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002323 if (change) {
2324 snd_hda_codec_write_cache(codec, 0x1d, 0,
2325 AC_VERB_SET_AMP_GAIN_MUTE,
2326 AMP_IN_UNMUTE(0));
2327 snd_hda_codec_write_cache(codec, 0x1d, 0,
2328 AC_VERB_SET_AMP_GAIN_MUTE,
2329 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002330 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002331 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002332 sel = snd_hda_codec_read(codec, 0x1d, 0,
2333 AC_VERB_GET_AMP_GAIN_MUTE,
2334 AC_AMP_GET_INPUT | 0x01);
2335 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002336 if (change) {
2337 snd_hda_codec_write_cache(codec, 0x1d, 0,
2338 AC_VERB_SET_AMP_GAIN_MUTE,
2339 AMP_IN_MUTE(0));
2340 snd_hda_codec_write_cache(codec, 0x1d, 0,
2341 AC_VERB_SET_AMP_GAIN_MUTE,
2342 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002343 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002344 sel = snd_hda_codec_read(codec, 0x0b, 0,
2345 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2346 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002347 if (change)
2348 snd_hda_codec_write_cache(codec, 0x0b, 0,
2349 AC_VERB_SET_CONNECT_SEL,
2350 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002351 }
2352 return change;
2353}
2354
2355static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
2356 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2357 {
2358 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2359 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002360 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002361 .info = ad1988_spdif_playback_source_info,
2362 .get = ad1988_spdif_playback_source_get,
2363 .put = ad1988_spdif_playback_source_put,
2364 },
2365 { } /* end */
2366};
2367
2368static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
2369 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2370 { } /* end */
2371};
2372
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002373static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
2374 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002375 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002376 { } /* end */
2377};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002378
2379/*
2380 * initialization verbs
2381 */
2382
2383/*
2384 * for 6-stack (+dig)
2385 */
2386static struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002387 /* Front, Surround, CLFE, side DAC; unmute as default */
2388 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2389 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2390 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2391 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002392 /* Port-A front headphon path */
2393 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2394 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2395 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2396 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2397 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2398 /* Port-D line-out path */
2399 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2400 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2401 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2402 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2403 /* Port-F surround path */
2404 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2405 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2406 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2407 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2408 /* Port-G CLFE path */
2409 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2410 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2411 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2412 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2413 /* Port-H side path */
2414 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2415 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2416 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2417 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2418 /* Mono out path */
2419 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2420 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2421 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2422 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2423 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2424 /* Port-B front mic-in path */
2425 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2426 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2427 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2428 /* Port-C line-in path */
2429 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2430 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2431 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2432 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2433 /* Port-E mic-in path */
2434 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2435 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2436 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2437 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002438 /* Analog CD Input */
2439 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002440 /* Analog Mix output amp */
2441 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002442
2443 { }
2444};
2445
2446static struct hda_verb ad1988_capture_init_verbs[] = {
2447 /* mute analog mix */
2448 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2449 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2450 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2451 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2452 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2453 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2454 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2455 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2456 /* select ADCs - front-mic */
2457 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2458 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2459 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002460
2461 { }
2462};
2463
2464static struct hda_verb ad1988_spdif_init_verbs[] = {
2465 /* SPDIF out sel */
2466 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2467 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2468 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002469 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002470 /* SPDIF out pin */
2471 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002472
2473 { }
2474};
2475
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002476static struct hda_verb ad1988_spdif_in_init_verbs[] = {
2477 /* unmute SPDIF input pin */
2478 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2479 { }
2480};
2481
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002482/* AD1989 has no ADC -> SPDIF route */
2483static struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002484 /* SPDIF-1 out pin */
2485 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002486 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002487 /* SPDIF-2/HDMI out pin */
2488 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2489 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002490 { }
2491};
2492
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002493/*
2494 * verbs for 3stack (+dig)
2495 */
2496static struct hda_verb ad1988_3stack_ch2_init[] = {
2497 /* set port-C to line-in */
2498 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2499 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2500 /* set port-E to mic-in */
2501 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2502 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2503 { } /* end */
2504};
2505
2506static struct hda_verb ad1988_3stack_ch6_init[] = {
2507 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002508 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002509 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002510 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002511 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002512 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002513 { } /* end */
2514};
2515
2516static struct hda_channel_mode ad1988_3stack_modes[2] = {
2517 { 2, ad1988_3stack_ch2_init },
2518 { 6, ad1988_3stack_ch6_init },
2519};
2520
2521static struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002522 /* Front, Surround, CLFE, side DAC; unmute as default */
2523 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2524 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2525 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2526 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002527 /* Port-A front headphon path */
2528 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2529 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2530 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2531 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2532 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2533 /* Port-D line-out path */
2534 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2535 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2536 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2537 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2538 /* Mono out path */
2539 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2540 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2541 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2542 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2543 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2544 /* Port-B front mic-in path */
2545 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2546 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2547 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002548 /* Port-C line-in/surround path - 6ch mode as default */
2549 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2550 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002551 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002552 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002553 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002554 /* Port-E mic-in/CLFE path - 6ch mode as default */
2555 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2556 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002557 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002558 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002559 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2560 /* mute analog mix */
2561 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2562 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2563 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2564 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2565 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2566 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2567 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2568 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2569 /* select ADCs - front-mic */
2570 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2571 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2572 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002573 /* Analog Mix output amp */
2574 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002575 { }
2576};
2577
2578/*
2579 * verbs for laptop mode (+dig)
2580 */
2581static struct hda_verb ad1988_laptop_hp_on[] = {
2582 /* unmute port-A and mute port-D */
2583 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2584 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2585 { } /* end */
2586};
2587static struct hda_verb ad1988_laptop_hp_off[] = {
2588 /* mute port-A and unmute port-D */
2589 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2590 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2591 { } /* end */
2592};
2593
2594#define AD1988_HP_EVENT 0x01
2595
2596static struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002597 /* Front, Surround, CLFE, side DAC; unmute as default */
2598 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2599 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2600 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2601 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002602 /* Port-A front headphon path */
2603 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2604 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2605 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2606 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2607 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2608 /* unsolicited event for pin-sense */
2609 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2610 /* Port-D line-out path + EAPD */
2611 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2612 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2613 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2614 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2615 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2616 /* Mono out path */
2617 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2618 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2619 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2620 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2621 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2622 /* Port-B mic-in path */
2623 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2624 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2625 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2626 /* Port-C docking station - try to output */
2627 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2628 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2629 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2630 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2631 /* mute analog mix */
2632 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2633 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2634 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2635 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2636 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2637 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2638 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2639 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2640 /* select ADCs - mic */
2641 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2642 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2643 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002644 /* Analog Mix output amp */
2645 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002646 { }
2647};
2648
2649static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2650{
2651 if ((res >> 26) != AD1988_HP_EVENT)
2652 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01002653 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002654 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2655 else
2656 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2657}
2658
Takashi Iwaicb53c622007-08-10 17:21:45 +02002659#ifdef CONFIG_SND_HDA_POWER_SAVE
2660static struct hda_amp_list ad1988_loopbacks[] = {
2661 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2662 { 0x20, HDA_INPUT, 1 }, /* Line */
2663 { 0x20, HDA_INPUT, 4 }, /* Mic */
2664 { 0x20, HDA_INPUT, 6 }, /* CD */
2665 { } /* end */
2666};
2667#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002668
2669/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002670 * Automatic parse of I/O pins from the BIOS configuration
2671 */
2672
Takashi Iwaid32410b12005-11-24 16:06:23 +01002673enum {
2674 AD_CTL_WIDGET_VOL,
2675 AD_CTL_WIDGET_MUTE,
2676 AD_CTL_BIND_MUTE,
2677};
2678static struct snd_kcontrol_new ad1988_control_templates[] = {
2679 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2680 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2681 HDA_BIND_MUTE(NULL, 0, 0, 0),
2682};
2683
2684/* add dynamic controls */
2685static int add_control(struct ad198x_spec *spec, int type, const char *name,
2686 unsigned long val)
2687{
2688 struct snd_kcontrol_new *knew;
2689
Takashi Iwai603c4012008-07-30 15:01:44 +02002690 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2691 knew = snd_array_new(&spec->kctls);
2692 if (!knew)
2693 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002694 *knew = ad1988_control_templates[type];
2695 knew->name = kstrdup(name, GFP_KERNEL);
2696 if (! knew->name)
2697 return -ENOMEM;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +01002698 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01002699 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002700 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002701 return 0;
2702}
2703
2704#define AD1988_PIN_CD_NID 0x18
2705#define AD1988_PIN_BEEP_NID 0x10
2706
2707static hda_nid_t ad1988_mixer_nids[8] = {
2708 /* A B C D E F G H */
2709 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2710};
2711
2712static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2713{
2714 static hda_nid_t idx_to_dac[8] = {
2715 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002716 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002717 };
2718 static hda_nid_t idx_to_dac_rev2[8] = {
2719 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002720 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002721 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002722 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002723 return idx_to_dac_rev2[idx];
2724 else
2725 return idx_to_dac[idx];
2726}
2727
2728static hda_nid_t ad1988_boost_nids[8] = {
2729 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2730};
2731
2732static int ad1988_pin_idx(hda_nid_t nid)
2733{
2734 static hda_nid_t ad1988_io_pins[8] = {
2735 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2736 };
2737 int i;
2738 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2739 if (ad1988_io_pins[i] == nid)
2740 return i;
2741 return 0; /* should be -1 */
2742}
2743
2744static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2745{
2746 static int loopback_idx[8] = {
2747 2, 0, 1, 3, 4, 5, 1, 4
2748 };
2749 switch (nid) {
2750 case AD1988_PIN_CD_NID:
2751 return 6;
2752 default:
2753 return loopback_idx[ad1988_pin_idx(nid)];
2754 }
2755}
2756
2757static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2758{
2759 static int adc_idx[8] = {
2760 0, 1, 2, 8, 4, 3, 6, 7
2761 };
2762 switch (nid) {
2763 case AD1988_PIN_CD_NID:
2764 return 5;
2765 default:
2766 return adc_idx[ad1988_pin_idx(nid)];
2767 }
2768}
2769
2770/* fill in the dac_nids table from the parsed pin configuration */
2771static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2772 const struct auto_pin_cfg *cfg)
2773{
2774 struct ad198x_spec *spec = codec->spec;
2775 int i, idx;
2776
2777 spec->multiout.dac_nids = spec->private_dac_nids;
2778
2779 /* check the pins hardwired to audio widget */
2780 for (i = 0; i < cfg->line_outs; i++) {
2781 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2782 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2783 }
2784 spec->multiout.num_dacs = cfg->line_outs;
2785 return 0;
2786}
2787
2788/* add playback controls from the parsed DAC table */
2789static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2790 const struct auto_pin_cfg *cfg)
2791{
2792 char name[32];
2793 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2794 hda_nid_t nid;
2795 int i, err;
2796
2797 for (i = 0; i < cfg->line_outs; i++) {
2798 hda_nid_t dac = spec->multiout.dac_nids[i];
2799 if (! dac)
2800 continue;
2801 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2802 if (i == 2) {
2803 /* Center/LFE */
2804 err = add_control(spec, AD_CTL_WIDGET_VOL,
2805 "Center Playback Volume",
2806 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2807 if (err < 0)
2808 return err;
2809 err = add_control(spec, AD_CTL_WIDGET_VOL,
2810 "LFE Playback Volume",
2811 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2812 if (err < 0)
2813 return err;
2814 err = add_control(spec, AD_CTL_BIND_MUTE,
2815 "Center Playback Switch",
2816 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2817 if (err < 0)
2818 return err;
2819 err = add_control(spec, AD_CTL_BIND_MUTE,
2820 "LFE Playback Switch",
2821 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2822 if (err < 0)
2823 return err;
2824 } else {
2825 sprintf(name, "%s Playback Volume", chname[i]);
2826 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2827 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2828 if (err < 0)
2829 return err;
2830 sprintf(name, "%s Playback Switch", chname[i]);
2831 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2832 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2833 if (err < 0)
2834 return err;
2835 }
2836 }
2837 return 0;
2838}
2839
2840/* add playback controls for speaker and HP outputs */
2841static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2842 const char *pfx)
2843{
2844 struct ad198x_spec *spec = codec->spec;
2845 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02002846 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002847 char name[32];
2848
2849 if (! pin)
2850 return 0;
2851
2852 idx = ad1988_pin_idx(pin);
2853 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02002854 /* check whether the corresponding DAC was already taken */
2855 for (i = 0; i < spec->autocfg.line_outs; i++) {
2856 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2857 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
2858 if (dac == nid)
2859 break;
2860 }
2861 if (i >= spec->autocfg.line_outs) {
2862 /* specify the DAC as the extra output */
2863 if (!spec->multiout.hp_nid)
2864 spec->multiout.hp_nid = nid;
2865 else
2866 spec->multiout.extra_out_nid[0] = nid;
2867 /* control HP volume/switch on the output mixer amp */
2868 sprintf(name, "%s Playback Volume", pfx);
2869 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2870 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2871 if (err < 0)
2872 return err;
2873 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01002874 nid = ad1988_mixer_nids[idx];
2875 sprintf(name, "%s Playback Switch", pfx);
2876 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2877 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2878 return err;
2879 return 0;
2880}
2881
2882/* create input playback/capture controls for the given pin */
2883static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2884 const char *ctlname, int boost)
2885{
2886 char name[32];
2887 int err, idx;
2888
2889 sprintf(name, "%s Playback Volume", ctlname);
2890 idx = ad1988_pin_to_loopback_idx(pin);
2891 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2892 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2893 return err;
2894 sprintf(name, "%s Playback Switch", ctlname);
2895 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2896 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2897 return err;
2898 if (boost) {
2899 hda_nid_t bnid;
2900 idx = ad1988_pin_idx(pin);
2901 bnid = ad1988_boost_nids[idx];
2902 if (bnid) {
2903 sprintf(name, "%s Boost", ctlname);
2904 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2905 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2906
2907 }
2908 }
2909 return 0;
2910}
2911
2912/* create playback/capture controls for input pins */
2913static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2914 const struct auto_pin_cfg *cfg)
2915{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002916 struct hda_input_mux *imux = &spec->private_imux;
2917 int i, err;
2918
2919 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002920 err = new_analog_input(spec, cfg->input_pins[i],
2921 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002922 i <= AUTO_PIN_FRONT_MIC);
2923 if (err < 0)
2924 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002925 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002926 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2927 imux->num_items++;
2928 }
2929 imux->items[imux->num_items].label = "Mix";
2930 imux->items[imux->num_items].index = 9;
2931 imux->num_items++;
2932
2933 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2934 "Analog Mix Playback Volume",
2935 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2936 return err;
2937 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2938 "Analog Mix Playback Switch",
2939 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2940 return err;
2941
2942 return 0;
2943}
2944
2945static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2946 hda_nid_t nid, int pin_type,
2947 int dac_idx)
2948{
2949 /* set as output */
2950 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2951 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2952 switch (nid) {
2953 case 0x11: /* port-A - DAC 04 */
2954 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2955 break;
2956 case 0x14: /* port-B - DAC 06 */
2957 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2958 break;
2959 case 0x15: /* port-C - DAC 05 */
2960 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2961 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002962 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002963 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2964 break;
2965 case 0x13: /* mono - DAC 04 */
2966 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2967 break;
2968 }
2969}
2970
2971static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2972{
2973 struct ad198x_spec *spec = codec->spec;
2974 int i;
2975
2976 for (i = 0; i < spec->autocfg.line_outs; i++) {
2977 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2978 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2979 }
2980}
2981
2982static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2983{
2984 struct ad198x_spec *spec = codec->spec;
2985 hda_nid_t pin;
2986
Takashi Iwai82bc9552006-03-21 11:24:42 +01002987 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002988 if (pin) /* connect to front */
2989 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002990 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002991 if (pin) /* connect to front */
2992 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2993}
2994
2995static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2996{
2997 struct ad198x_spec *spec = codec->spec;
2998 int i, idx;
2999
3000 for (i = 0; i < AUTO_PIN_LAST; i++) {
3001 hda_nid_t nid = spec->autocfg.input_pins[i];
3002 if (! nid)
3003 continue;
3004 switch (nid) {
3005 case 0x15: /* port-C */
3006 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
3007 break;
3008 case 0x17: /* port-E */
3009 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
3010 break;
3011 }
3012 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
3013 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
3014 if (nid != AD1988_PIN_CD_NID)
3015 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
3016 AMP_OUT_MUTE);
3017 idx = ad1988_pin_idx(nid);
3018 if (ad1988_boost_nids[idx])
3019 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
3020 AC_VERB_SET_AMP_GAIN_MUTE,
3021 AMP_OUT_ZERO);
3022 }
3023}
3024
3025/* parse the BIOS configuration and set up the alc_spec */
3026/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
3027static int ad1988_parse_auto_config(struct hda_codec *codec)
3028{
3029 struct ad198x_spec *spec = codec->spec;
3030 int err;
3031
Kailang Yangdf694da2005-12-05 19:42:22 +01003032 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003033 return err;
3034 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
3035 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01003036 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003037 return 0; /* can't find valid BIOS pin config */
3038 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01003039 (err = ad1988_auto_create_extra_out(codec,
3040 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003041 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003042 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003043 "Headphone")) < 0 ||
3044 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
3045 return err;
3046
3047 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3048
Takashi Iwai0852d7a2009-02-11 11:35:15 +01003049 if (spec->autocfg.dig_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003050 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3051 if (spec->autocfg.dig_in_pin)
3052 spec->dig_in_nid = AD1988_SPDIF_IN;
3053
Takashi Iwai603c4012008-07-30 15:01:44 +02003054 if (spec->kctls.list)
3055 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003056
3057 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
3058
3059 spec->input_mux = &spec->private_imux;
3060
3061 return 1;
3062}
3063
3064/* init callback for auto-configuration model -- overriding the default init */
3065static int ad1988_auto_init(struct hda_codec *codec)
3066{
3067 ad198x_init(codec);
3068 ad1988_auto_init_multi_out(codec);
3069 ad1988_auto_init_extra_out(codec);
3070 ad1988_auto_init_analog_input(codec);
3071 return 0;
3072}
3073
3074
3075/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003076 */
3077
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003078static const char *ad1988_models[AD1988_MODEL_LAST] = {
3079 [AD1988_6STACK] = "6stack",
3080 [AD1988_6STACK_DIG] = "6stack-dig",
3081 [AD1988_3STACK] = "3stack",
3082 [AD1988_3STACK_DIG] = "3stack-dig",
3083 [AD1988_LAPTOP] = "laptop",
3084 [AD1988_LAPTOP_DIG] = "laptop-dig",
3085 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003086};
3087
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003088static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01003089 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01003090 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02003091 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07003092 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003093 {}
3094};
3095
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003096static int patch_ad1988(struct hda_codec *codec)
3097{
3098 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003099 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003100
3101 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3102 if (spec == NULL)
3103 return -ENOMEM;
3104
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003105 codec->spec = spec;
3106
Takashi Iwai1a806f42006-07-03 15:58:16 +02003107 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003108 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
3109
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003110 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003111 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003112 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02003113 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3114 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003115 board_config = AD1988_AUTO;
3116 }
3117
3118 if (board_config == AD1988_AUTO) {
3119 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003120 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003121 if (err < 0) {
3122 ad198x_free(codec);
3123 return err;
3124 } else if (! err) {
3125 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
3126 board_config = AD1988_6STACK;
3127 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003128 }
3129
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003130 err = snd_hda_attach_beep_device(codec, 0x10);
3131 if (err < 0) {
3132 ad198x_free(codec);
3133 return err;
3134 }
3135 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3136
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003137 switch (board_config) {
3138 case AD1988_6STACK:
3139 case AD1988_6STACK_DIG:
3140 spec->multiout.max_channels = 8;
3141 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003142 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003143 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3144 else
3145 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003146 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003147 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003148 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003149 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3150 else
3151 spec->mixers[0] = ad1988_6stack_mixers1;
3152 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003153 spec->num_init_verbs = 1;
3154 spec->init_verbs[0] = ad1988_6stack_init_verbs;
3155 if (board_config == AD1988_6STACK_DIG) {
3156 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3157 spec->dig_in_nid = AD1988_SPDIF_IN;
3158 }
3159 break;
3160 case AD1988_3STACK:
3161 case AD1988_3STACK_DIG:
3162 spec->multiout.max_channels = 6;
3163 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003164 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003165 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3166 else
3167 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003168 spec->input_mux = &ad1988_6stack_capture_source;
3169 spec->channel_mode = ad1988_3stack_modes;
3170 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003171 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003172 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003173 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3174 else
3175 spec->mixers[0] = ad1988_3stack_mixers1;
3176 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003177 spec->num_init_verbs = 1;
3178 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3179 if (board_config == AD1988_3STACK_DIG)
3180 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3181 break;
3182 case AD1988_LAPTOP:
3183 case AD1988_LAPTOP_DIG:
3184 spec->multiout.max_channels = 2;
3185 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003186 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003187 spec->input_mux = &ad1988_laptop_capture_source;
3188 spec->num_mixers = 1;
3189 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01003190 spec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003191 spec->num_init_verbs = 1;
3192 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3193 if (board_config == AD1988_LAPTOP_DIG)
3194 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3195 break;
3196 }
3197
Takashi Iwaid32410b12005-11-24 16:06:23 +01003198 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3199 spec->adc_nids = ad1988_adc_nids;
3200 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003201 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3202 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3203 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003204 if (codec->vendor_id >= 0x11d4989a) {
3205 spec->mixers[spec->num_mixers++] =
3206 ad1989_spdif_out_mixers;
3207 spec->init_verbs[spec->num_init_verbs++] =
3208 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003209 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003210 } else {
3211 spec->mixers[spec->num_mixers++] =
3212 ad1988_spdif_out_mixers;
3213 spec->init_verbs[spec->num_init_verbs++] =
3214 ad1988_spdif_init_verbs;
3215 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003216 }
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003217 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003218 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003219 spec->init_verbs[spec->num_init_verbs++] =
3220 ad1988_spdif_in_init_verbs;
3221 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003222
3223 codec->patch_ops = ad198x_patch_ops;
3224 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003225 case AD1988_AUTO:
3226 codec->patch_ops.init = ad1988_auto_init;
3227 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003228 case AD1988_LAPTOP:
3229 case AD1988_LAPTOP_DIG:
3230 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3231 break;
3232 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003233#ifdef CONFIG_SND_HDA_POWER_SAVE
3234 spec->loopback.amplist = ad1988_loopbacks;
3235#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003236 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003237
Takashi Iwai729d55b2009-12-25 22:49:01 +01003238 codec->no_trigger_sense = 1;
3239
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003240 return 0;
3241}
3242
3243
3244/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003245 * AD1884 / AD1984
3246 *
3247 * port-B - front line/mic-in
3248 * port-E - aux in/out
3249 * port-F - aux in/out
3250 * port-C - rear line/mic-in
3251 * port-D - rear line/hp-out
3252 * port-A - front line/hp-out
3253 *
3254 * AD1984 = AD1884 + two digital mic-ins
3255 *
3256 * FIXME:
3257 * For simplicity, we share the single DAC for both HP and line-outs
3258 * right now. The inidividual playbacks could be easily implemented,
3259 * but no build-up framework is given, so far.
3260 */
3261
3262static hda_nid_t ad1884_dac_nids[1] = {
3263 0x04,
3264};
3265
3266static hda_nid_t ad1884_adc_nids[2] = {
3267 0x08, 0x09,
3268};
3269
3270static hda_nid_t ad1884_capsrc_nids[2] = {
3271 0x0c, 0x0d,
3272};
3273
3274#define AD1884_SPDIF_OUT 0x02
3275
3276static struct hda_input_mux ad1884_capture_source = {
3277 .num_items = 4,
3278 .items = {
3279 { "Front Mic", 0x0 },
3280 { "Mic", 0x1 },
3281 { "CD", 0x2 },
3282 { "Mix", 0x3 },
3283 },
3284};
3285
3286static struct snd_kcontrol_new ad1884_base_mixers[] = {
3287 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3288 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3289 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3290 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3291 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3292 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3293 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3294 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3295 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3296 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3297 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3298 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003299 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
3300 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3301 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3302 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3303 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3304 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3305 {
3306 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3307 /* The multiple "Capture Source" controls confuse alsamixer
3308 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003309 */
3310 /* .name = "Capture Source", */
3311 .name = "Input Source",
3312 .count = 2,
3313 .info = ad198x_mux_enum_info,
3314 .get = ad198x_mux_enum_get,
3315 .put = ad198x_mux_enum_put,
3316 },
3317 /* SPDIF controls */
3318 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3319 {
3320 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3321 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3322 /* identical with ad1983 */
3323 .info = ad1983_spdif_route_info,
3324 .get = ad1983_spdif_route_get,
3325 .put = ad1983_spdif_route_put,
3326 },
3327 { } /* end */
3328};
3329
3330static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3331 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3332 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3333 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003334 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003335 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003336 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003337 { } /* end */
3338};
3339
3340/*
3341 * initialization verbs
3342 */
3343static struct hda_verb ad1884_init_verbs[] = {
3344 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003345 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3346 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003347 /* Port-A (HP) mixer */
3348 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3349 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3350 /* Port-A pin */
3351 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3352 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3353 /* HP selector - select DAC2 */
3354 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3355 /* Port-D (Line-out) mixer */
3356 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3357 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3358 /* Port-D pin */
3359 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3360 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3361 /* Mono-out mixer */
3362 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3363 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3364 /* Mono-out pin */
3365 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3366 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3367 /* Mono selector */
3368 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3369 /* Port-B (front mic) pin */
3370 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003371 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003372 /* Port-C (rear mic) pin */
3373 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003374 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003375 /* Analog mixer; mute as default */
3376 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3377 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3378 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3379 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3380 /* Analog Mix output amp */
3381 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3382 /* SPDIF output selector */
3383 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3384 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3385 { } /* end */
3386};
3387
Takashi Iwaicb53c622007-08-10 17:21:45 +02003388#ifdef CONFIG_SND_HDA_POWER_SAVE
3389static struct hda_amp_list ad1884_loopbacks[] = {
3390 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3391 { 0x20, HDA_INPUT, 1 }, /* Mic */
3392 { 0x20, HDA_INPUT, 2 }, /* CD */
3393 { 0x20, HDA_INPUT, 4 }, /* Docking */
3394 { } /* end */
3395};
3396#endif
3397
Takashi Iwai2134ea42008-01-10 16:53:55 +01003398static const char *ad1884_slave_vols[] = {
3399 "PCM Playback Volume",
3400 "Mic Playback Volume",
3401 "Mono Playback Volume",
3402 "Front Mic Playback Volume",
3403 "Mic Playback Volume",
3404 "CD Playback Volume",
3405 "Internal Mic Playback Volume",
Akinobu Mitabca68462009-04-06 18:42:42 +09003406 "Docking Mic Playback Volume",
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003407 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003408 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003409 NULL
3410};
3411
Takashi Iwai2bac6472007-05-18 18:21:41 +02003412static int patch_ad1884(struct hda_codec *codec)
3413{
3414 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003415 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003416
3417 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3418 if (spec == NULL)
3419 return -ENOMEM;
3420
Takashi Iwai2bac6472007-05-18 18:21:41 +02003421 codec->spec = spec;
3422
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003423 err = snd_hda_attach_beep_device(codec, 0x10);
3424 if (err < 0) {
3425 ad198x_free(codec);
3426 return err;
3427 }
3428 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3429
Takashi Iwai2bac6472007-05-18 18:21:41 +02003430 spec->multiout.max_channels = 2;
3431 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3432 spec->multiout.dac_nids = ad1884_dac_nids;
3433 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3434 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3435 spec->adc_nids = ad1884_adc_nids;
3436 spec->capsrc_nids = ad1884_capsrc_nids;
3437 spec->input_mux = &ad1884_capture_source;
3438 spec->num_mixers = 1;
3439 spec->mixers[0] = ad1884_base_mixers;
3440 spec->num_init_verbs = 1;
3441 spec->init_verbs[0] = ad1884_init_verbs;
3442 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003443#ifdef CONFIG_SND_HDA_POWER_SAVE
3444 spec->loopback.amplist = ad1884_loopbacks;
3445#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003446 spec->vmaster_nid = 0x04;
3447 /* we need to cover all playback volumes */
3448 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003449
3450 codec->patch_ops = ad198x_patch_ops;
3451
Takashi Iwai729d55b2009-12-25 22:49:01 +01003452 codec->no_trigger_sense = 1;
3453
Takashi Iwai2bac6472007-05-18 18:21:41 +02003454 return 0;
3455}
3456
3457/*
3458 * Lenovo Thinkpad T61/X61
3459 */
3460static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003461 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003462 .items = {
3463 { "Mic", 0x0 },
3464 { "Internal Mic", 0x1 },
3465 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003466 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003467 },
3468};
3469
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003470
3471/*
3472 * Dell Precision T3400
3473 */
3474static struct hda_input_mux ad1984_dell_desktop_capture_source = {
3475 .num_items = 3,
3476 .items = {
3477 { "Front Mic", 0x0 },
3478 { "Line-In", 0x1 },
3479 { "Mix", 0x3 },
3480 },
3481};
3482
3483
Takashi Iwai2bac6472007-05-18 18:21:41 +02003484static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3485 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3486 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3487 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3488 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3489 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3490 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3491 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3492 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003493 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3494 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003495 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3496 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003497 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3498 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003499 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003500 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3501 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3502 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3503 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3504 {
3505 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3506 /* The multiple "Capture Source" controls confuse alsamixer
3507 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003508 */
3509 /* .name = "Capture Source", */
3510 .name = "Input Source",
3511 .count = 2,
3512 .info = ad198x_mux_enum_info,
3513 .get = ad198x_mux_enum_get,
3514 .put = ad198x_mux_enum_put,
3515 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003516 /* SPDIF controls */
3517 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3518 {
3519 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3520 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3521 /* identical with ad1983 */
3522 .info = ad1983_spdif_route_info,
3523 .get = ad1983_spdif_route_get,
3524 .put = ad1983_spdif_route_put,
3525 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003526 { } /* end */
3527};
3528
3529/* additional verbs */
3530static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3531 /* Port-E (docking station mic) pin */
3532 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3533 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3534 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003535 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003536 /* Analog PC Beeper - allow firmware/ACPI beeps */
3537 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003538 /* Analog mixer - docking mic; mute as default */
3539 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003540 /* enable EAPD bit */
3541 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003542 { } /* end */
3543};
3544
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003545/*
3546 * Dell Precision T3400
3547 */
3548static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
3549 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3550 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3551 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3552 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3553 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3554 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3555 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3556 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3557 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003558 HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
3559 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3560 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3561 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3562 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3563 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3564 {
3565 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3566 /* The multiple "Capture Source" controls confuse alsamixer
3567 * So call somewhat different..
3568 */
3569 /* .name = "Capture Source", */
3570 .name = "Input Source",
3571 .count = 2,
3572 .info = ad198x_mux_enum_info,
3573 .get = ad198x_mux_enum_get,
3574 .put = ad198x_mux_enum_put,
3575 },
3576 { } /* end */
3577};
3578
Takashi Iwai2bac6472007-05-18 18:21:41 +02003579/* Digial MIC ADC NID 0x05 + 0x06 */
3580static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3581 struct hda_codec *codec,
3582 unsigned int stream_tag,
3583 unsigned int format,
3584 struct snd_pcm_substream *substream)
3585{
3586 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3587 stream_tag, 0, format);
3588 return 0;
3589}
3590
3591static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3592 struct hda_codec *codec,
3593 struct snd_pcm_substream *substream)
3594{
Takashi Iwai888afa12008-03-18 09:57:50 +01003595 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003596 return 0;
3597}
3598
3599static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3600 .substreams = 2,
3601 .channels_min = 2,
3602 .channels_max = 2,
3603 .nid = 0x05,
3604 .ops = {
3605 .prepare = ad1984_pcm_dmic_prepare,
3606 .cleanup = ad1984_pcm_dmic_cleanup
3607 },
3608};
3609
3610static int ad1984_build_pcms(struct hda_codec *codec)
3611{
3612 struct ad198x_spec *spec = codec->spec;
3613 struct hda_pcm *info;
3614 int err;
3615
3616 err = ad198x_build_pcms(codec);
3617 if (err < 0)
3618 return err;
3619
3620 info = spec->pcm_rec + codec->num_pcms;
3621 codec->num_pcms++;
3622 info->name = "AD1984 Digital Mic";
3623 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3624 return 0;
3625}
3626
3627/* models */
3628enum {
3629 AD1984_BASIC,
3630 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003631 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003632 AD1984_MODELS
3633};
3634
3635static const char *ad1984_models[AD1984_MODELS] = {
3636 [AD1984_BASIC] = "basic",
3637 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003638 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003639};
3640
3641static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3642 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003643 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003644 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003645 {}
3646};
3647
3648static int patch_ad1984(struct hda_codec *codec)
3649{
3650 struct ad198x_spec *spec;
3651 int board_config, err;
3652
3653 err = patch_ad1884(codec);
3654 if (err < 0)
3655 return err;
3656 spec = codec->spec;
3657 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3658 ad1984_models, ad1984_cfg_tbl);
3659 switch (board_config) {
3660 case AD1984_BASIC:
3661 /* additional digital mics */
3662 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3663 codec->patch_ops.build_pcms = ad1984_build_pcms;
3664 break;
3665 case AD1984_THINKPAD:
Jerone Youngebf00c52008-01-07 12:22:18 +01003666 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003667 spec->input_mux = &ad1984_thinkpad_capture_source;
3668 spec->mixers[0] = ad1984_thinkpad_mixers;
3669 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003670 spec->analog_beep = 1;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003671 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003672 case AD1984_DELL_DESKTOP:
3673 spec->multiout.dig_out_nid = 0;
3674 spec->input_mux = &ad1984_dell_desktop_capture_source;
3675 spec->mixers[0] = ad1984_dell_desktop_mixers;
3676 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003677 }
3678 return 0;
3679}
3680
3681
3682/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003683 * AD1883 / AD1884A / AD1984A / AD1984B
3684 *
3685 * port-B (0x14) - front mic-in
3686 * port-E (0x1c) - rear mic-in
3687 * port-F (0x16) - CD / ext out
3688 * port-C (0x15) - rear line-in
3689 * port-D (0x12) - rear line-out
3690 * port-A (0x11) - front hp-out
3691 *
3692 * AD1984A = AD1884A + digital-mic
3693 * AD1883 = equivalent with AD1984A
3694 * AD1984B = AD1984A + extra SPDIF-out
3695 *
3696 * FIXME:
3697 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3698 */
3699
3700static hda_nid_t ad1884a_dac_nids[1] = {
3701 0x03,
3702};
3703
3704#define ad1884a_adc_nids ad1884_adc_nids
3705#define ad1884a_capsrc_nids ad1884_capsrc_nids
3706
3707#define AD1884A_SPDIF_OUT 0x02
3708
3709static struct hda_input_mux ad1884a_capture_source = {
3710 .num_items = 5,
3711 .items = {
3712 { "Front Mic", 0x0 },
3713 { "Mic", 0x4 },
3714 { "Line", 0x1 },
3715 { "CD", 0x2 },
3716 { "Mix", 0x3 },
3717 },
3718};
3719
3720static struct snd_kcontrol_new ad1884a_base_mixers[] = {
3721 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3722 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3723 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3724 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3725 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3726 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3727 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3728 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3729 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3730 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3731 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3732 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3733 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3734 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3735 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3736 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003737 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3738 HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
3739 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3740 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3741 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3742 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3743 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3744 {
3745 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3746 /* The multiple "Capture Source" controls confuse alsamixer
3747 * So call somewhat different..
3748 */
3749 /* .name = "Capture Source", */
3750 .name = "Input Source",
3751 .count = 2,
3752 .info = ad198x_mux_enum_info,
3753 .get = ad198x_mux_enum_get,
3754 .put = ad198x_mux_enum_put,
3755 },
3756 /* SPDIF controls */
3757 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3758 {
3759 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3760 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3761 /* identical with ad1983 */
3762 .info = ad1983_spdif_route_info,
3763 .get = ad1983_spdif_route_get,
3764 .put = ad1983_spdif_route_put,
3765 },
3766 { } /* end */
3767};
3768
3769/*
3770 * initialization verbs
3771 */
3772static struct hda_verb ad1884a_init_verbs[] = {
3773 /* DACs; unmute as default */
3774 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3775 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3776 /* Port-A (HP) mixer - route only from analog mixer */
3777 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3778 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3779 /* Port-A pin */
3780 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3781 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3782 /* Port-D (Line-out) mixer - route only from analog mixer */
3783 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3784 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3785 /* Port-D pin */
3786 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3787 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3788 /* Mono-out mixer - route only from analog mixer */
3789 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3790 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3791 /* Mono-out pin */
3792 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3793 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3794 /* Port-B (front mic) pin */
3795 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003796 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003797 /* Port-C (rear line-in) pin */
3798 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003799 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003800 /* Port-E (rear mic) pin */
3801 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3802 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3803 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
3804 /* Port-F (CD) pin */
3805 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3806 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3807 /* Analog mixer; mute as default */
3808 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3809 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3810 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3811 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3812 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
3813 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3814 /* Analog Mix output amp */
3815 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3816 /* capture sources */
3817 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
3818 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3819 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3820 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3821 /* SPDIF output amp */
3822 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3823 { } /* end */
3824};
3825
3826#ifdef CONFIG_SND_HDA_POWER_SAVE
3827static struct hda_amp_list ad1884a_loopbacks[] = {
3828 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3829 { 0x20, HDA_INPUT, 1 }, /* Mic */
3830 { 0x20, HDA_INPUT, 2 }, /* CD */
3831 { 0x20, HDA_INPUT, 4 }, /* Docking */
3832 { } /* end */
3833};
3834#endif
3835
3836/*
3837 * Laptop model
3838 *
3839 * Port A: Headphone jack
3840 * Port B: MIC jack
3841 * Port C: Internal MIC
3842 * Port D: Dock Line Out (if enabled)
3843 * Port E: Dock Line In (if enabled)
3844 * Port F: Internal speakers
3845 */
3846
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003847static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
3848 struct snd_ctl_elem_value *ucontrol)
3849{
3850 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3851 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
3852 int mute = (!ucontrol->value.integer.value[0] &&
3853 !ucontrol->value.integer.value[1]);
3854 /* toggle GPIO1 according to the mute state */
3855 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
3856 mute ? 0x02 : 0x0);
3857 return ret;
3858}
Takashi Iwaic5059252008-02-16 09:43:56 +01003859
3860static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
3861 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003862 {
3863 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3864 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003865 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003866 .info = snd_hda_mixer_amp_switch_info,
3867 .get = snd_hda_mixer_amp_switch_get,
3868 .put = ad1884a_mobile_master_sw_put,
3869 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3870 },
Takashi Iwaic5059252008-02-16 09:43:56 +01003871 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3872 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3873 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3874 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3875 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3876 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3877 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3878 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3879 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003880 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3881 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
3882 HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3883 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3884 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003885 { } /* end */
3886};
3887
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003888static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
3889 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db172009-07-02 16:10:23 +02003890 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
3891 {
3892 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3893 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003894 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai099db172009-07-02 16:10:23 +02003895 .info = snd_hda_mixer_amp_switch_info,
3896 .get = snd_hda_mixer_amp_switch_get,
3897 .put = ad1884a_mobile_master_sw_put,
3898 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3899 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003900 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3901 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02003902 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
3903 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003904 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3905 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003906 { } /* end */
3907};
3908
Takashi Iwaic5059252008-02-16 09:43:56 +01003909/* mute internal speaker if HP is plugged */
3910static void ad1884a_hp_automute(struct hda_codec *codec)
3911{
3912 unsigned int present;
3913
Takashi Iwaid56757a2009-11-18 08:00:14 +01003914 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01003915 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3916 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3917 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3918 present ? 0x00 : 0x02);
3919}
3920
Takashi Iwai269ef192008-05-30 15:32:15 +02003921/* switch to external mic if plugged */
3922static void ad1884a_hp_automic(struct hda_codec *codec)
3923{
3924 unsigned int present;
3925
Takashi Iwaid56757a2009-11-18 08:00:14 +01003926 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02003927 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
3928 present ? 0 : 1);
3929}
3930
Takashi Iwaic5059252008-02-16 09:43:56 +01003931#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02003932#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01003933
3934/* unsolicited event for HP jack sensing */
3935static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
3936{
Takashi Iwai269ef192008-05-30 15:32:15 +02003937 switch (res >> 26) {
3938 case AD1884A_HP_EVENT:
3939 ad1884a_hp_automute(codec);
3940 break;
3941 case AD1884A_MIC_EVENT:
3942 ad1884a_hp_automic(codec);
3943 break;
3944 }
Takashi Iwaic5059252008-02-16 09:43:56 +01003945}
3946
3947/* initialize jack-sensing, too */
3948static int ad1884a_hp_init(struct hda_codec *codec)
3949{
3950 ad198x_init(codec);
3951 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02003952 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01003953 return 0;
3954}
3955
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003956/* mute internal speaker if HP or docking HP is plugged */
3957static void ad1884a_laptop_automute(struct hda_codec *codec)
3958{
3959 unsigned int present;
3960
Takashi Iwaid56757a2009-11-18 08:00:14 +01003961 present = snd_hda_jack_detect(codec, 0x11);
3962 if (!present)
3963 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003964 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3965 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3966 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3967 present ? 0x00 : 0x02);
3968}
3969
3970/* switch to external mic if plugged */
3971static void ad1884a_laptop_automic(struct hda_codec *codec)
3972{
3973 unsigned int idx;
3974
Takashi Iwaid56757a2009-11-18 08:00:14 +01003975 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003976 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01003977 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003978 idx = 4;
3979 else
3980 idx = 1;
3981 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
3982}
3983
3984/* unsolicited event for HP jack sensing */
3985static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
3986 unsigned int res)
3987{
3988 switch (res >> 26) {
3989 case AD1884A_HP_EVENT:
3990 ad1884a_laptop_automute(codec);
3991 break;
3992 case AD1884A_MIC_EVENT:
3993 ad1884a_laptop_automic(codec);
3994 break;
3995 }
3996}
3997
3998/* initialize jack-sensing, too */
3999static int ad1884a_laptop_init(struct hda_codec *codec)
4000{
4001 ad198x_init(codec);
4002 ad1884a_laptop_automute(codec);
4003 ad1884a_laptop_automic(codec);
4004 return 0;
4005}
4006
Takashi Iwaic5059252008-02-16 09:43:56 +01004007/* additional verbs for laptop model */
4008static struct hda_verb ad1884a_laptop_verbs[] = {
4009 /* Port-A (HP) pin - always unmuted */
4010 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4011 /* Port-F (int speaker) mixer - route only from analog mixer */
4012 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4013 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08004014 /* Port-F (int speaker) pin */
4015 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01004016 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08004017 /* required for compaq 6530s/6531s speaker output */
4018 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004019 /* Port-C pin - internal mic-in */
4020 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4021 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4022 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02004023 /* Port-D (docking line-out) pin - default unmuted */
4024 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01004025 /* analog mix */
4026 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4027 /* unsolicited event for pin-sense */
4028 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004029 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004030 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004031 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02004032 /* allow to touch GPIO1 (for mute control) */
4033 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4034 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4035 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01004036 { } /* end */
4037};
4038
Takashi Iwai73156132009-04-23 08:24:48 +02004039static struct hda_verb ad1884a_mobile_verbs[] = {
4040 /* DACs; unmute as default */
4041 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4042 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4043 /* Port-A (HP) mixer - route only from analog mixer */
4044 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4045 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4046 /* Port-A pin */
4047 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4048 /* Port-A (HP) pin - always unmuted */
4049 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4050 /* Port-B (mic jack) pin */
4051 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4052 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4053 /* Port-C (int mic) pin */
4054 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4055 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4056 /* Port-F (int speaker) mixer - route only from analog mixer */
4057 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4058 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4059 /* Port-F pin */
4060 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4061 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4062 /* Analog mixer; mute as default */
4063 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4064 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4065 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4066 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4067 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4068 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4069 /* Analog Mix output amp */
4070 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4071 /* capture sources */
4072 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4073 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4074 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4075 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4076 /* unsolicited event for pin-sense */
4077 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4078 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db172009-07-02 16:10:23 +02004079 /* allow to touch GPIO1 (for mute control) */
4080 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4081 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4082 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02004083 { } /* end */
4084};
4085
Takashi Iwaic5059252008-02-16 09:43:56 +01004086/*
Takashi Iwaif0813742008-03-18 12:13:03 +01004087 * Thinkpad X300
4088 * 0x11 - HP
4089 * 0x12 - speaker
4090 * 0x14 - mic-in
4091 * 0x17 - built-in mic
4092 */
4093
4094static struct hda_verb ad1984a_thinkpad_verbs[] = {
4095 /* HP unmute */
4096 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4097 /* analog mix */
4098 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4099 /* turn on EAPD */
4100 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4101 /* unsolicited event for pin-sense */
4102 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4103 /* internal mic - dmic */
4104 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02004105 /* set magic COEFs for dmic */
4106 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4107 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01004108 { } /* end */
4109};
4110
4111static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
4112 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4113 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4114 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4115 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4116 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4117 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01004118 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
4119 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4120 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4121 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4122 {
4123 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4124 .name = "Capture Source",
4125 .info = ad198x_mux_enum_info,
4126 .get = ad198x_mux_enum_get,
4127 .put = ad198x_mux_enum_put,
4128 },
4129 { } /* end */
4130};
4131
4132static struct hda_input_mux ad1984a_thinkpad_capture_source = {
4133 .num_items = 3,
4134 .items = {
4135 { "Mic", 0x0 },
4136 { "Internal Mic", 0x5 },
4137 { "Mix", 0x3 },
4138 },
4139};
4140
4141/* mute internal speaker if HP is plugged */
4142static void ad1984a_thinkpad_automute(struct hda_codec *codec)
4143{
4144 unsigned int present;
4145
Takashi Iwaid56757a2009-11-18 08:00:14 +01004146 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01004147 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
4148 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4149}
4150
4151/* unsolicited event for HP jack sensing */
4152static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
4153 unsigned int res)
4154{
4155 if ((res >> 26) != AD1884A_HP_EVENT)
4156 return;
4157 ad1984a_thinkpad_automute(codec);
4158}
4159
4160/* initialize jack-sensing, too */
4161static int ad1984a_thinkpad_init(struct hda_codec *codec)
4162{
4163 ad198x_init(codec);
4164 ad1984a_thinkpad_automute(codec);
4165 return 0;
4166}
4167
4168/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004169 * HP Touchsmart
4170 * port-A (0x11) - front hp-out
4171 * port-B (0x14) - unused
4172 * port-C (0x15) - unused
4173 * port-D (0x12) - rear line out
4174 * port-E (0x1c) - front mic-in
4175 * port-F (0x16) - Internal speakers
4176 * digital-mic (0x17) - Internal mic
4177 */
4178
4179static struct hda_verb ad1984a_touchsmart_verbs[] = {
4180 /* DACs; unmute as default */
4181 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4182 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4183 /* Port-A (HP) mixer - route only from analog mixer */
4184 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4185 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4186 /* Port-A pin */
4187 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4188 /* Port-A (HP) pin - always unmuted */
4189 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4190 /* Port-E (int speaker) mixer - route only from analog mixer */
4191 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4192 /* Port-E pin */
4193 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4194 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4195 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4196 /* Port-F (int speaker) mixer - route only from analog mixer */
4197 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4198 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4199 /* Port-F pin */
4200 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4201 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4202 /* Analog mixer; mute as default */
4203 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4204 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4205 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4206 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4207 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4208 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4209 /* Analog Mix output amp */
4210 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4211 /* capture sources */
4212 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4213 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4214 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4215 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4216 /* unsolicited event for pin-sense */
4217 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4218 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4219 /* allow to touch GPIO1 (for mute control) */
4220 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4221 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4222 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4223 /* internal mic - dmic */
4224 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4225 /* set magic COEFs for dmic */
4226 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4227 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4228 { } /* end */
4229};
4230
4231static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
4232 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4233/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4234 {
4235 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004236 .subdevice = HDA_SUBDEV_AMP_FLAG,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004237 .name = "Master Playback Switch",
4238 .info = snd_hda_mixer_amp_switch_info,
4239 .get = snd_hda_mixer_amp_switch_get,
4240 .put = ad1884a_mobile_master_sw_put,
4241 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4242 },
4243 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4244 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4245 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4246 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4247 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
4248 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4249 { } /* end */
4250};
4251
4252/* switch to external mic if plugged */
4253static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4254{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004255 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004256 snd_hda_codec_write(codec, 0x0c, 0,
4257 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004258 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004259 snd_hda_codec_write(codec, 0x0c, 0,
4260 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004261}
4262
4263
4264/* unsolicited event for HP jack sensing */
4265static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4266 unsigned int res)
4267{
4268 switch (res >> 26) {
4269 case AD1884A_HP_EVENT:
4270 ad1884a_hp_automute(codec);
4271 break;
4272 case AD1884A_MIC_EVENT:
4273 ad1984a_touchsmart_automic(codec);
4274 break;
4275 }
4276}
4277
4278/* initialize jack-sensing, too */
4279static int ad1984a_touchsmart_init(struct hda_codec *codec)
4280{
4281 ad198x_init(codec);
4282 ad1884a_hp_automute(codec);
4283 ad1984a_touchsmart_automic(codec);
4284 return 0;
4285}
4286
4287
4288/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004289 */
4290
4291enum {
4292 AD1884A_DESKTOP,
4293 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004294 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004295 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004296 AD1984A_TOUCHSMART,
Takashi Iwaic5059252008-02-16 09:43:56 +01004297 AD1884A_MODELS
4298};
4299
4300static const char *ad1884a_models[AD1884A_MODELS] = {
4301 [AD1884A_DESKTOP] = "desktop",
4302 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004303 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004304 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004305 [AD1984A_TOUCHSMART] = "touchsmart",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004306};
4307
4308static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
4309 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004310 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004311 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004312 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004313 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004314 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4315 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004316 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004317 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004318 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004319 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004320};
4321
4322static int patch_ad1884a(struct hda_codec *codec)
4323{
4324 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004325 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004326
4327 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4328 if (spec == NULL)
4329 return -ENOMEM;
4330
Takashi Iwaic5059252008-02-16 09:43:56 +01004331 codec->spec = spec;
4332
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004333 err = snd_hda_attach_beep_device(codec, 0x10);
4334 if (err < 0) {
4335 ad198x_free(codec);
4336 return err;
4337 }
4338 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4339
Takashi Iwaic5059252008-02-16 09:43:56 +01004340 spec->multiout.max_channels = 2;
4341 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4342 spec->multiout.dac_nids = ad1884a_dac_nids;
4343 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4344 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4345 spec->adc_nids = ad1884a_adc_nids;
4346 spec->capsrc_nids = ad1884a_capsrc_nids;
4347 spec->input_mux = &ad1884a_capture_source;
4348 spec->num_mixers = 1;
4349 spec->mixers[0] = ad1884a_base_mixers;
4350 spec->num_init_verbs = 1;
4351 spec->init_verbs[0] = ad1884a_init_verbs;
4352 spec->spdif_route = 0;
4353#ifdef CONFIG_SND_HDA_POWER_SAVE
4354 spec->loopback.amplist = ad1884a_loopbacks;
4355#endif
4356 codec->patch_ops = ad198x_patch_ops;
4357
4358 /* override some parameters */
4359 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004360 ad1884a_models,
4361 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01004362 switch (board_config) {
4363 case AD1884A_LAPTOP:
4364 spec->mixers[0] = ad1884a_laptop_mixers;
4365 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4366 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004367 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4368 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004369 /* set the upper-limit for mixer amp to 0dB for avoiding the
4370 * possible damage by overloading
4371 */
4372 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4373 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4374 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4375 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4376 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004377 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004378 case AD1884A_MOBILE:
4379 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004380 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004381 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004382 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4383 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004384 /* set the upper-limit for mixer amp to 0dB for avoiding the
4385 * possible damage by overloading
4386 */
4387 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4388 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4389 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4390 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4391 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004392 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004393 case AD1884A_THINKPAD:
4394 spec->mixers[0] = ad1984a_thinkpad_mixers;
4395 spec->init_verbs[spec->num_init_verbs++] =
4396 ad1984a_thinkpad_verbs;
4397 spec->multiout.dig_out_nid = 0;
4398 spec->input_mux = &ad1984a_thinkpad_capture_source;
4399 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4400 codec->patch_ops.init = ad1984a_thinkpad_init;
4401 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004402 case AD1984A_TOUCHSMART:
4403 spec->mixers[0] = ad1984a_touchsmart_mixers;
4404 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4405 spec->multiout.dig_out_nid = 0;
4406 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4407 codec->patch_ops.init = ad1984a_touchsmart_init;
4408 /* set the upper-limit for mixer amp to 0dB for avoiding the
4409 * possible damage by overloading
4410 */
4411 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4412 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4413 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4414 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4415 (1 << AC_AMPCAP_MUTE_SHIFT));
4416 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004417 }
4418
Takashi Iwai729d55b2009-12-25 22:49:01 +01004419 codec->no_trigger_sense = 1;
4420
Takashi Iwaic5059252008-02-16 09:43:56 +01004421 return 0;
4422}
4423
4424
4425/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004426 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004427 *
4428 * port-A - front hp-out
4429 * port-B - front mic-in
4430 * port-C - rear line-in, shared surr-out (3stack)
4431 * port-D - rear line-out
4432 * port-E - rear mic-in, shared clfe-out (3stack)
4433 * port-F - rear surr-out (6stack)
4434 * port-G - rear clfe-out (6stack)
4435 */
4436
4437static hda_nid_t ad1882_dac_nids[3] = {
4438 0x04, 0x03, 0x05
4439};
4440
4441static hda_nid_t ad1882_adc_nids[2] = {
4442 0x08, 0x09,
4443};
4444
4445static hda_nid_t ad1882_capsrc_nids[2] = {
4446 0x0c, 0x0d,
4447};
4448
4449#define AD1882_SPDIF_OUT 0x02
4450
4451/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
4452static struct hda_input_mux ad1882_capture_source = {
4453 .num_items = 5,
4454 .items = {
4455 { "Front Mic", 0x1 },
4456 { "Mic", 0x4 },
4457 { "Line", 0x2 },
4458 { "CD", 0x3 },
4459 { "Mix", 0x7 },
4460 },
4461};
4462
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004463/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
4464static struct hda_input_mux ad1882a_capture_source = {
4465 .num_items = 5,
4466 .items = {
4467 { "Front Mic", 0x1 },
4468 { "Mic", 0x4},
4469 { "Line", 0x2 },
4470 { "Digital Mic", 0x06 },
4471 { "Mix", 0x7 },
4472 },
4473};
4474
Takashi Iwai0ac85512007-06-20 15:46:13 +02004475static struct snd_kcontrol_new ad1882_base_mixers[] = {
4476 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4477 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4478 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4479 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4480 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4481 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4482 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4483 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004484
Takashi Iwai0ac85512007-06-20 15:46:13 +02004485 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
4486 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
4487 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
4488 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4489 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4490 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4491 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4492 {
4493 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4494 /* The multiple "Capture Source" controls confuse alsamixer
4495 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004496 */
4497 /* .name = "Capture Source", */
4498 .name = "Input Source",
4499 .count = 2,
4500 .info = ad198x_mux_enum_info,
4501 .get = ad198x_mux_enum_get,
4502 .put = ad198x_mux_enum_put,
4503 },
4504 /* SPDIF controls */
4505 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4506 {
4507 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4508 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4509 /* identical with ad1983 */
4510 .info = ad1983_spdif_route_info,
4511 .get = ad1983_spdif_route_get,
4512 .put = ad1983_spdif_route_put,
4513 },
4514 { } /* end */
4515};
4516
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004517static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
4518 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4519 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4520 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4521 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4522 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4523 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4524 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4525 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004526 { } /* end */
4527};
4528
4529static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
4530 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4531 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4532 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4533 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4534 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4535 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4536 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4537 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004538 HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
4539 { } /* end */
4540};
4541
Takashi Iwai0ac85512007-06-20 15:46:13 +02004542static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
4543 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4544 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4545 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4546 {
4547 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4548 .name = "Channel Mode",
4549 .info = ad198x_ch_mode_info,
4550 .get = ad198x_ch_mode_get,
4551 .put = ad198x_ch_mode_put,
4552 },
4553 { } /* end */
4554};
4555
4556static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
4557 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4558 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4559 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4560 { } /* end */
4561};
4562
4563static struct hda_verb ad1882_ch2_init[] = {
4564 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4565 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4566 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4567 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4568 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4569 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4570 { } /* end */
4571};
4572
4573static struct hda_verb ad1882_ch4_init[] = {
4574 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4575 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4576 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4577 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4578 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4579 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4580 { } /* end */
4581};
4582
4583static struct hda_verb ad1882_ch6_init[] = {
4584 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4585 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4586 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4587 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4588 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4589 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4590 { } /* end */
4591};
4592
4593static struct hda_channel_mode ad1882_modes[3] = {
4594 { 2, ad1882_ch2_init },
4595 { 4, ad1882_ch4_init },
4596 { 6, ad1882_ch6_init },
4597};
4598
4599/*
4600 * initialization verbs
4601 */
4602static struct hda_verb ad1882_init_verbs[] = {
4603 /* DACs; mute as default */
4604 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4605 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4606 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4607 /* Port-A (HP) mixer */
4608 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4609 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4610 /* Port-A pin */
4611 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4612 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4613 /* HP selector - select DAC2 */
4614 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4615 /* Port-D (Line-out) mixer */
4616 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4617 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4618 /* Port-D pin */
4619 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4620 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4621 /* Mono-out mixer */
4622 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4623 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4624 /* Mono-out pin */
4625 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4626 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4627 /* Port-B (front mic) pin */
4628 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4629 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4630 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4631 /* Port-C (line-in) pin */
4632 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4633 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4634 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4635 /* Port-C mixer - mute as input */
4636 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4637 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4638 /* Port-E (mic-in) pin */
4639 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4640 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4641 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4642 /* Port-E mixer - mute as input */
4643 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4644 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4645 /* Port-F (surround) */
4646 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4647 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4648 /* Port-G (CLFE) */
4649 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4650 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4651 /* Analog mixer; mute as default */
4652 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4653 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4654 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4655 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4656 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4657 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4658 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4659 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4660 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4661 /* Analog Mix output amp */
4662 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4663 /* SPDIF output selector */
4664 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4665 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4666 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4667 { } /* end */
4668};
4669
Takashi Iwaicb53c622007-08-10 17:21:45 +02004670#ifdef CONFIG_SND_HDA_POWER_SAVE
4671static struct hda_amp_list ad1882_loopbacks[] = {
4672 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4673 { 0x20, HDA_INPUT, 1 }, /* Mic */
4674 { 0x20, HDA_INPUT, 4 }, /* Line */
4675 { 0x20, HDA_INPUT, 6 }, /* CD */
4676 { } /* end */
4677};
4678#endif
4679
Takashi Iwai0ac85512007-06-20 15:46:13 +02004680/* models */
4681enum {
4682 AD1882_3STACK,
4683 AD1882_6STACK,
4684 AD1882_MODELS
4685};
4686
4687static const char *ad1882_models[AD1986A_MODELS] = {
4688 [AD1882_3STACK] = "3stack",
4689 [AD1882_6STACK] = "6stack",
4690};
4691
4692
4693static int patch_ad1882(struct hda_codec *codec)
4694{
4695 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004696 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004697
4698 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4699 if (spec == NULL)
4700 return -ENOMEM;
4701
Takashi Iwai0ac85512007-06-20 15:46:13 +02004702 codec->spec = spec;
4703
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004704 err = snd_hda_attach_beep_device(codec, 0x10);
4705 if (err < 0) {
4706 ad198x_free(codec);
4707 return err;
4708 }
4709 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4710
Takashi Iwai0ac85512007-06-20 15:46:13 +02004711 spec->multiout.max_channels = 6;
4712 spec->multiout.num_dacs = 3;
4713 spec->multiout.dac_nids = ad1882_dac_nids;
4714 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
4715 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
4716 spec->adc_nids = ad1882_adc_nids;
4717 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004718 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004719 spec->input_mux = &ad1882_capture_source;
4720 else
4721 spec->input_mux = &ad1882a_capture_source;
4722 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004723 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004724 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004725 spec->mixers[1] = ad1882_loopback_mixers;
4726 else
4727 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004728 spec->num_init_verbs = 1;
4729 spec->init_verbs[0] = ad1882_init_verbs;
4730 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02004731#ifdef CONFIG_SND_HDA_POWER_SAVE
4732 spec->loopback.amplist = ad1882_loopbacks;
4733#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01004734 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004735
4736 codec->patch_ops = ad198x_patch_ops;
4737
4738 /* override some parameters */
4739 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
4740 ad1882_models, NULL);
4741 switch (board_config) {
4742 default:
4743 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004744 spec->num_mixers = 3;
4745 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004746 spec->channel_mode = ad1882_modes;
4747 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
4748 spec->need_dac_fix = 1;
4749 spec->multiout.max_channels = 2;
4750 spec->multiout.num_dacs = 1;
4751 break;
4752 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004753 spec->num_mixers = 3;
4754 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004755 break;
4756 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01004757
4758 codec->no_trigger_sense = 1;
4759
Takashi Iwai0ac85512007-06-20 15:46:13 +02004760 return 0;
4761}
4762
4763
4764/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004765 * patch entries
4766 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004767static struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004768 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02004769 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004770 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004771 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004772 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
4773 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02004774 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
4775 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004776 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004777 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01004778 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02004779 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004780 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02004781 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
4782 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004783 {} /* terminator */
4784};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004785
4786MODULE_ALIAS("snd-hda-codec-id:11d4*");
4787
4788MODULE_LICENSE("GPL");
4789MODULE_DESCRIPTION("Analog Devices HD-audio codec");
4790
4791static struct hda_codec_preset_list analog_list = {
4792 .preset = snd_hda_preset_analog,
4793 .owner = THIS_MODULE,
4794};
4795
4796static int __init patch_analog_init(void)
4797{
4798 return snd_hda_add_codec_preset(&analog_list);
4799}
4800
4801static void __exit patch_analog_exit(void)
4802{
4803 snd_hda_delete_codec_preset(&analog_list);
4804}
4805
4806module_init(patch_analog_init)
4807module_exit(patch_analog_exit)