blob: 10bbbaf6ebc3d2b0531ba2fb1ab7e3f2be1f8994 [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}
Daniel T Chenea52bf22009-12-27 18:48:29 -0500530#endif
531
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200532static struct hda_codec_ops ad198x_patch_ops = {
533 .build_controls = ad198x_build_controls,
534 .build_pcms = ad198x_build_pcms,
535 .init = ad198x_init,
536 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200537#ifdef CONFIG_SND_HDA_POWER_SAVE
538 .check_power_status = ad198x_check_power_status,
539#endif
Daniel T Chenea52bf22009-12-27 18:48:29 -0500540#ifdef SND_HDA_NEEDS_RESUME
541 .suspend = ad198x_suspend,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500542#endif
543 .reboot_notify = ad198x_shutup,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200544};
545
546
547/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100548 * EAPD control
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100549 * the private value = nid
Takashi Iwai18a815d2006-03-01 19:54:39 +0100550 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200551#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100552
553static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
554 struct snd_ctl_elem_value *ucontrol)
555{
556 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
557 struct ad198x_spec *spec = codec->spec;
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100558 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100559 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
560 else
561 ucontrol->value.integer.value[0] = spec->cur_eapd;
562 return 0;
563}
564
565static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
566 struct snd_ctl_elem_value *ucontrol)
567{
568 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
569 struct ad198x_spec *spec = codec->spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +0100570 hda_nid_t nid = kcontrol->private_value & 0xff;
571 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100572 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100573 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100574 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200575 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100576 return 0;
577 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200578 snd_hda_codec_write_cache(codec, nid,
579 0, AC_VERB_SET_EAPD_BTLENABLE,
580 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100581 return 1;
582}
583
Takashi Iwai9230d212006-03-13 13:49:49 +0100584static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
585 struct snd_ctl_elem_info *uinfo);
586static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
587 struct snd_ctl_elem_value *ucontrol);
588static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
589 struct snd_ctl_elem_value *ucontrol);
590
591
Takashi Iwai18a815d2006-03-01 19:54:39 +0100592/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200593 * AD1986A specific
594 */
595
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596#define AD1986A_SPDIF_OUT 0x02
597#define AD1986A_FRONT_DAC 0x03
598#define AD1986A_SURR_DAC 0x04
599#define AD1986A_CLFE_DAC 0x05
600#define AD1986A_ADC 0x06
601
602static hda_nid_t ad1986a_dac_nids[3] = {
603 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
604};
Takashi Iwai985be542005-11-02 18:26:49 +0100605static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +0100606static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607
608static struct hda_input_mux ad1986a_capture_source = {
609 .num_items = 7,
610 .items = {
611 { "Mic", 0x0 },
612 { "CD", 0x1 },
613 { "Aux", 0x3 },
614 { "Line", 0x4 },
615 { "Mix", 0x5 },
616 { "Mono", 0x6 },
617 { "Phone", 0x7 },
618 },
619};
620
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621
Takashi Iwai532d5382007-07-27 19:02:40 +0200622static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
623 .ops = &snd_hda_bind_vol,
624 .values = {
625 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
626 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
627 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
628 0
629 },
630};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631
Takashi Iwai532d5382007-07-27 19:02:40 +0200632static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
633 .ops = &snd_hda_bind_sw,
634 .values = {
635 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
636 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
637 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
638 0
639 },
640};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641
642/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 * mixers
644 */
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100645static struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200646 /*
647 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
648 */
649 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
650 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
652 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
653 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
654 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
655 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
656 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
657 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
658 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
659 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
660 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
661 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
662 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
663 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
664 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
665 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
666 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
667 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
668 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100669 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
671 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
672 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
673 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
674 {
675 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
676 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200677 .info = ad198x_mux_enum_info,
678 .get = ad198x_mux_enum_get,
679 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 },
681 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
682 { } /* end */
683};
684
Takashi Iwai9230d212006-03-13 13:49:49 +0100685/* additional mixers for 3stack mode */
686static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
687 {
688 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
689 .name = "Channel Mode",
690 .info = ad198x_ch_mode_info,
691 .get = ad198x_ch_mode_get,
692 .put = ad198x_ch_mode_put,
693 },
694 { } /* end */
695};
696
697/* laptop model - 2ch only */
698static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
699
Takashi Iwai20a45e82007-08-15 22:20:45 +0200700/* master controls both pins 0x1a and 0x1b */
701static struct hda_bind_ctls ad1986a_laptop_master_vol = {
702 .ops = &snd_hda_bind_vol,
703 .values = {
704 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
705 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
706 0,
707 },
708};
709
710static struct hda_bind_ctls ad1986a_laptop_master_sw = {
711 .ops = &snd_hda_bind_sw,
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
Takashi Iwai9230d212006-03-13 13:49:49 +0100719static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
720 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
721 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200722 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
723 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100724 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
725 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
726 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
727 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
728 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
729 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
730 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
731 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100732 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100733 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100734 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
735 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
736 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
737 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
738 {
739 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
740 .name = "Capture Source",
741 .info = ad198x_mux_enum_info,
742 .get = ad198x_mux_enum_get,
743 .put = ad198x_mux_enum_put,
744 },
745 { } /* end */
746};
747
Takashi Iwai825aa972006-03-17 10:50:49 +0100748/* laptop-eapd model - 2ch only */
749
Takashi Iwai825aa972006-03-17 10:50:49 +0100750static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
751 .num_items = 3,
752 .items = {
753 { "Mic", 0x0 },
754 { "Internal Mic", 0x4 },
755 { "Mix", 0x5 },
756 },
757};
758
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100759static struct hda_input_mux ad1986a_automic_capture_source = {
760 .num_items = 2,
761 .items = {
762 { "Mic", 0x0 },
763 { "Mix", 0x5 },
764 },
765};
766
Takashi Iwai16d11a82009-06-24 14:07:53 +0200767static struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200768 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
769 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai16d11a82009-06-24 14:07:53 +0200770 { } /* end */
771};
772
773static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai825aa972006-03-17 10:50:49 +0100774 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
775 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100776 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
777 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
778 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
779 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
780 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
781 {
782 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
783 .name = "Capture Source",
784 .info = ad198x_mux_enum_info,
785 .get = ad198x_mux_enum_get,
786 .put = ad198x_mux_enum_put,
787 },
788 {
789 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
790 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100791 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwai1725b822008-11-21 02:25:48 +0100792 .info = ad198x_eapd_info,
793 .get = ad198x_eapd_get,
794 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100795 .private_value = 0x1b, /* port-D */
Takashi Iwai1725b822008-11-21 02:25:48 +0100796 },
797 { } /* end */
798};
799
Takashi Iwai16d11a82009-06-24 14:07:53 +0200800static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
801 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
802 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
Takashi Iwai825aa972006-03-17 10:50:49 +0100803 { } /* end */
804};
805
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100806/* re-connect the mic boost input according to the jack sensing */
807static void ad1986a_automic(struct hda_codec *codec)
808{
809 unsigned int present;
Takashi Iwaid56757a2009-11-18 08:00:14 +0100810 present = snd_hda_jack_detect(codec, 0x1f);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100811 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
812 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
Takashi Iwaid56757a2009-11-18 08:00:14 +0100813 present ? 0 : 2);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100814}
815
816#define AD1986A_MIC_EVENT 0x36
817
818static void ad1986a_automic_unsol_event(struct hda_codec *codec,
819 unsigned int res)
820{
821 if ((res >> 26) != AD1986A_MIC_EVENT)
822 return;
823 ad1986a_automic(codec);
824}
825
826static int ad1986a_automic_init(struct hda_codec *codec)
827{
828 ad198x_init(codec);
829 ad1986a_automic(codec);
830 return 0;
831}
832
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200833/* laptop-automute - 2ch only */
834
835static void ad1986a_update_hp(struct hda_codec *codec)
836{
837 struct ad198x_spec *spec = codec->spec;
838 unsigned int mute;
839
840 if (spec->jack_present)
841 mute = HDA_AMP_MUTE; /* mute internal speaker */
842 else
843 /* unmute internal speaker if necessary */
844 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
845 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
846 HDA_AMP_MUTE, mute);
847}
848
849static void ad1986a_hp_automute(struct hda_codec *codec)
850{
851 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200852
Takashi Iwaid56757a2009-11-18 08:00:14 +0100853 spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
Takashi Iwai03c405a2009-06-24 14:10:15 +0200854 if (spec->inv_jack_detect)
855 spec->jack_present = !spec->jack_present;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200856 ad1986a_update_hp(codec);
857}
858
859#define AD1986A_HP_EVENT 0x37
860
861static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
862{
863 if ((res >> 26) != AD1986A_HP_EVENT)
864 return;
865 ad1986a_hp_automute(codec);
866}
867
868static int ad1986a_hp_init(struct hda_codec *codec)
869{
870 ad198x_init(codec);
871 ad1986a_hp_automute(codec);
872 return 0;
873}
874
875/* bind hp and internal speaker mute (with plug check) */
876static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
877 struct snd_ctl_elem_value *ucontrol)
878{
879 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
880 long *valp = ucontrol->value.integer.value;
881 int change;
882
883 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
884 HDA_AMP_MUTE,
885 valp[0] ? 0 : HDA_AMP_MUTE);
886 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
887 HDA_AMP_MUTE,
888 valp[1] ? 0 : HDA_AMP_MUTE);
889 if (change)
890 ad1986a_update_hp(codec);
891 return change;
892}
893
Takashi Iwai16d11a82009-06-24 14:07:53 +0200894static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200895 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
896 {
897 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
898 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100899 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200900 .info = snd_hda_mixer_amp_switch_info,
901 .get = snd_hda_mixer_amp_switch_get,
902 .put = ad1986a_hp_master_sw_put,
903 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
904 },
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200905 { } /* end */
906};
907
Takashi Iwai16d11a82009-06-24 14:07:53 +0200908
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909/*
910 * initialization verbs
911 */
912static struct hda_verb ad1986a_init_verbs[] = {
913 /* Front, Surround, CLFE DAC; mute as default */
914 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
915 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
916 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
917 /* Downmix - off */
918 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
919 /* HP, Line-Out, Surround, CLFE selectors */
920 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
921 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
922 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
923 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
924 /* Mono selector */
925 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
926 /* Mic selector: Mic 1/2 pin */
927 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
928 /* Line-in selector: Line-in */
929 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
930 /* Mic 1/2 swap */
931 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
932 /* Record selector: mic */
933 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
934 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
935 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
936 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
937 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
938 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
939 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
940 /* PC beep */
941 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
942 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
943 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
944 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
945 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
946 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
947 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200948 /* HP Pin */
949 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
950 /* Front, Surround, CLFE Pins */
951 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
952 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
953 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
954 /* Mono Pin */
955 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
956 /* Mic Pin */
957 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
958 /* Line, Aux, CD, Beep-In Pin */
959 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
960 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
961 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
962 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
963 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 { } /* end */
965};
966
Takashi Iwai9230d212006-03-13 13:49:49 +0100967static struct hda_verb ad1986a_ch2_init[] = {
968 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200969 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
970 /* Line-in selectors */
971 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100972 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200973 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
974 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
975 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100976 { } /* end */
977};
978
979static struct hda_verb ad1986a_ch4_init[] = {
980 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200981 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
982 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100983 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200984 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
985 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100986 { } /* end */
987};
988
989static struct hda_verb ad1986a_ch6_init[] = {
990 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200991 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
992 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100993 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200994 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
995 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100996 { } /* end */
997};
998
999static struct hda_channel_mode ad1986a_modes[3] = {
1000 { 2, ad1986a_ch2_init },
1001 { 4, ad1986a_ch4_init },
1002 { 6, ad1986a_ch6_init },
1003};
1004
Takashi Iwai825aa972006-03-17 10:50:49 +01001005/* eapd initialization */
1006static struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001007 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa972006-03-17 10:50:49 +01001008 {}
1009};
1010
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001011static struct hda_verb ad1986a_automic_verbs[] = {
1012 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1013 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1014 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
1015 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
1016 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
1017 {}
1018};
1019
Tobin Davisf36090f2007-01-08 11:07:12 +01001020/* Ultra initialization */
1021static struct hda_verb ad1986a_ultra_init[] = {
1022 /* eapd initialization */
1023 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
1024 /* CLFE -> Mic in */
1025 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
1026 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1027 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
1028 { } /* end */
1029};
1030
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001031/* pin sensing on HP jack */
1032static struct hda_verb ad1986a_hp_init_verbs[] = {
1033 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
1034 {}
1035};
1036
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001037static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
1038 unsigned int res)
1039{
1040 switch (res >> 26) {
1041 case AD1986A_HP_EVENT:
1042 ad1986a_hp_automute(codec);
1043 break;
1044 case AD1986A_MIC_EVENT:
1045 ad1986a_automic(codec);
1046 break;
1047 }
1048}
1049
1050static int ad1986a_samsung_p50_init(struct hda_codec *codec)
1051{
1052 ad198x_init(codec);
1053 ad1986a_hp_automute(codec);
1054 ad1986a_automic(codec);
1055 return 0;
1056}
1057
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001058
Takashi Iwai9230d212006-03-13 13:49:49 +01001059/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001060enum {
1061 AD1986A_6STACK,
1062 AD1986A_3STACK,
1063 AD1986A_LAPTOP,
1064 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001065 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +01001066 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +01001067 AD1986A_SAMSUNG,
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001068 AD1986A_SAMSUNG_P50,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001069 AD1986A_MODELS
1070};
Takashi Iwai9230d212006-03-13 13:49:49 +01001071
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001072static const char *ad1986a_models[AD1986A_MODELS] = {
1073 [AD1986A_6STACK] = "6stack",
1074 [AD1986A_3STACK] = "3stack",
1075 [AD1986A_LAPTOP] = "laptop",
1076 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001077 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +01001078 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +01001079 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001080 [AD1986A_SAMSUNG_P50] = "samsung-p50",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001081};
1082
1083static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
1084 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001085 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001086 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001087 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001088 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1089 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1090 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1091 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001092 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001093 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001094 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1095 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1096 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1097 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1098 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001099 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Daniel T Chenba579eb2010-02-20 11:16:30 -05001100 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001101 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001102 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001103 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
Tobin Davisf36090f2007-01-08 11:07:12 +01001104 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001105 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001106 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001107 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001108 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001109 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001110 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001111 {}
1112};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113
Takashi Iwaicb53c622007-08-10 17:21:45 +02001114#ifdef CONFIG_SND_HDA_POWER_SAVE
1115static struct hda_amp_list ad1986a_loopbacks[] = {
1116 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1117 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1118 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1119 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1120 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1121 { } /* end */
1122};
1123#endif
1124
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001125static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1126{
Takashi Iwai2f334f92009-02-20 14:37:42 +01001127 unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001128 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1129}
1130
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131static int patch_ad1986a(struct hda_codec *codec)
1132{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001133 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001134 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001136 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 if (spec == NULL)
1138 return -ENOMEM;
1139
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 codec->spec = spec;
1141
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001142 err = snd_hda_attach_beep_device(codec, 0x19);
1143 if (err < 0) {
1144 ad198x_free(codec);
1145 return err;
1146 }
1147 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1148
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 spec->multiout.max_channels = 6;
1150 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1151 spec->multiout.dac_nids = ad1986a_dac_nids;
1152 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001153 spec->num_adc_nids = 1;
1154 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001155 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001156 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001157 spec->num_mixers = 1;
1158 spec->mixers[0] = ad1986a_mixers;
1159 spec->num_init_verbs = 1;
1160 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001161#ifdef CONFIG_SND_HDA_POWER_SAVE
1162 spec->loopback.amplist = ad1986a_loopbacks;
1163#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001164 spec->vmaster_nid = 0x1b;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01001165 spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001167 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168
Takashi Iwai9230d212006-03-13 13:49:49 +01001169 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001170 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1171 ad1986a_models,
1172 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001173 switch (board_config) {
1174 case AD1986A_3STACK:
1175 spec->num_mixers = 2;
1176 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001177 spec->num_init_verbs = 2;
1178 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001179 spec->channel_mode = ad1986a_modes;
1180 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001181 spec->need_dac_fix = 1;
1182 spec->multiout.max_channels = 2;
1183 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001184 break;
1185 case AD1986A_LAPTOP:
1186 spec->mixers[0] = ad1986a_laptop_mixers;
1187 spec->multiout.max_channels = 2;
1188 spec->multiout.num_dacs = 1;
1189 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1190 break;
Takashi Iwai825aa972006-03-17 10:50:49 +01001191 case AD1986A_LAPTOP_EAPD:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001192 spec->num_mixers = 3;
1193 spec->mixers[0] = ad1986a_laptop_master_mixers;
1194 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1195 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001196 spec->num_init_verbs = 2;
1197 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1198 spec->multiout.max_channels = 2;
1199 spec->multiout.num_dacs = 1;
1200 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1201 if (!is_jack_available(codec, 0x25))
1202 spec->multiout.dig_out_nid = 0;
1203 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1204 break;
1205 case AD1986A_SAMSUNG:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001206 spec->num_mixers = 2;
1207 spec->mixers[0] = ad1986a_laptop_master_mixers;
1208 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001209 spec->num_init_verbs = 3;
Takashi Iwai825aa972006-03-17 10:50:49 +01001210 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001211 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa972006-03-17 10:50:49 +01001212 spec->multiout.max_channels = 2;
1213 spec->multiout.num_dacs = 1;
1214 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001215 if (!is_jack_available(codec, 0x25))
1216 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001217 spec->input_mux = &ad1986a_automic_capture_source;
1218 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1219 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa972006-03-17 10:50:49 +01001220 break;
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001221 case AD1986A_SAMSUNG_P50:
1222 spec->num_mixers = 2;
1223 spec->mixers[0] = ad1986a_automute_master_mixers;
1224 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1225 spec->num_init_verbs = 4;
1226 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1227 spec->init_verbs[2] = ad1986a_automic_verbs;
1228 spec->init_verbs[3] = ad1986a_hp_init_verbs;
1229 spec->multiout.max_channels = 2;
1230 spec->multiout.num_dacs = 1;
1231 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1232 if (!is_jack_available(codec, 0x25))
1233 spec->multiout.dig_out_nid = 0;
1234 spec->input_mux = &ad1986a_automic_capture_source;
1235 codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
1236 codec->patch_ops.init = ad1986a_samsung_p50_init;
1237 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001238 case AD1986A_LAPTOP_AUTOMUTE:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001239 spec->num_mixers = 3;
1240 spec->mixers[0] = ad1986a_automute_master_mixers;
1241 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1242 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001243 spec->num_init_verbs = 3;
1244 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1245 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1246 spec->multiout.max_channels = 2;
1247 spec->multiout.num_dacs = 1;
1248 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001249 if (!is_jack_available(codec, 0x25))
1250 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001251 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1252 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1253 codec->patch_ops.init = ad1986a_hp_init;
Takashi Iwai03c405a2009-06-24 14:10:15 +02001254 /* Lenovo N100 seems to report the reversed bit
1255 * for HP jack-sensing
1256 */
1257 spec->inv_jack_detect = 1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001258 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001259 case AD1986A_ULTRA:
1260 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1261 spec->num_init_verbs = 2;
1262 spec->init_verbs[1] = ad1986a_ultra_init;
1263 spec->multiout.max_channels = 2;
1264 spec->multiout.num_dacs = 1;
1265 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1266 spec->multiout.dig_out_nid = 0;
1267 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001268 }
1269
Takashi Iwaid29240c2007-10-26 12:35:56 +02001270 /* AD1986A has a hardware problem that it can't share a stream
1271 * with multiple output pins. The copy of front to surrounds
1272 * causes noisy or silent outputs at a certain timing, e.g.
1273 * changing the volume.
1274 * So, let's disable the shared stream.
1275 */
1276 spec->multiout.no_share_stream = 1;
1277
Takashi Iwai729d55b2009-12-25 22:49:01 +01001278 codec->no_trigger_sense = 1;
1279
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 return 0;
1281}
1282
1283/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001284 * AD1983 specific
1285 */
1286
1287#define AD1983_SPDIF_OUT 0x02
1288#define AD1983_DAC 0x03
1289#define AD1983_ADC 0x04
1290
1291static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001292static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001293static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001294
1295static struct hda_input_mux ad1983_capture_source = {
1296 .num_items = 4,
1297 .items = {
1298 { "Mic", 0x0 },
1299 { "Line", 0x1 },
1300 { "Mix", 0x2 },
1301 { "Mix Mono", 0x3 },
1302 },
1303};
1304
1305/*
1306 * SPDIF playback route
1307 */
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001308static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001309{
1310 static char *texts[] = { "PCM", "ADC" };
1311
1312 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1313 uinfo->count = 1;
1314 uinfo->value.enumerated.items = 2;
1315 if (uinfo->value.enumerated.item > 1)
1316 uinfo->value.enumerated.item = 1;
1317 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1318 return 0;
1319}
1320
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001321static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001322{
1323 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1324 struct ad198x_spec *spec = codec->spec;
1325
1326 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1327 return 0;
1328}
1329
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001330static int ad1983_spdif_route_put(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
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001335 if (ucontrol->value.enumerated.item[0] > 1)
1336 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001337 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1338 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001339 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1340 AC_VERB_SET_CONNECT_SEL,
1341 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001342 return 1;
1343 }
1344 return 0;
1345}
1346
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001347static struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001348 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1349 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1350 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1351 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1352 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1353 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1354 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1355 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1356 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1357 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1358 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1359 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001360 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
1361 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1362 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1363 {
1364 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1365 .name = "Capture Source",
1366 .info = ad198x_mux_enum_info,
1367 .get = ad198x_mux_enum_get,
1368 .put = ad198x_mux_enum_put,
1369 },
1370 {
1371 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001372 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001373 .info = ad1983_spdif_route_info,
1374 .get = ad1983_spdif_route_get,
1375 .put = ad1983_spdif_route_put,
1376 },
1377 { } /* end */
1378};
1379
1380static struct hda_verb ad1983_init_verbs[] = {
1381 /* Front, HP, Mono; mute as default */
1382 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1383 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1384 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1385 /* Beep, PCM, Mic, Line-In: mute */
1386 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1387 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1388 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1389 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1390 /* Front, HP selectors; from Mix */
1391 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1392 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1393 /* Mono selector; from Mix */
1394 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1395 /* Mic selector; Mic */
1396 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1397 /* Line-in selector: Line-in */
1398 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1399 /* Mic boost: 0dB */
1400 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1401 /* Record selector: mic */
1402 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1403 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1404 /* SPDIF route: PCM */
1405 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1406 /* Front Pin */
1407 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1408 /* HP Pin */
1409 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1410 /* Mono Pin */
1411 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1412 /* Mic Pin */
1413 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1414 /* Line Pin */
1415 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1416 { } /* end */
1417};
1418
Takashi Iwaicb53c622007-08-10 17:21:45 +02001419#ifdef CONFIG_SND_HDA_POWER_SAVE
1420static struct hda_amp_list ad1983_loopbacks[] = {
1421 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1422 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1423 { } /* end */
1424};
1425#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001426
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001427static int patch_ad1983(struct hda_codec *codec)
1428{
1429 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001430 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001431
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001432 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001433 if (spec == NULL)
1434 return -ENOMEM;
1435
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001436 codec->spec = spec;
1437
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001438 err = snd_hda_attach_beep_device(codec, 0x10);
1439 if (err < 0) {
1440 ad198x_free(codec);
1441 return err;
1442 }
1443 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1444
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001445 spec->multiout.max_channels = 2;
1446 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1447 spec->multiout.dac_nids = ad1983_dac_nids;
1448 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001449 spec->num_adc_nids = 1;
1450 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001451 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001452 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001453 spec->num_mixers = 1;
1454 spec->mixers[0] = ad1983_mixers;
1455 spec->num_init_verbs = 1;
1456 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001457 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001458#ifdef CONFIG_SND_HDA_POWER_SAVE
1459 spec->loopback.amplist = ad1983_loopbacks;
1460#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001461 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001462
1463 codec->patch_ops = ad198x_patch_ops;
1464
Takashi Iwai729d55b2009-12-25 22:49:01 +01001465 codec->no_trigger_sense = 1;
1466
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001467 return 0;
1468}
1469
1470
1471/*
1472 * AD1981 HD specific
1473 */
1474
1475#define AD1981_SPDIF_OUT 0x02
1476#define AD1981_DAC 0x03
1477#define AD1981_ADC 0x04
1478
1479static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001480static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001481static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001482
1483/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
1484static struct hda_input_mux ad1981_capture_source = {
1485 .num_items = 7,
1486 .items = {
1487 { "Front Mic", 0x0 },
1488 { "Line", 0x1 },
1489 { "Mix", 0x2 },
1490 { "Mix Mono", 0x3 },
1491 { "CD", 0x4 },
1492 { "Mic", 0x6 },
1493 { "Aux", 0x7 },
1494 },
1495};
1496
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001497static struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001498 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1499 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1500 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1501 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1502 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1503 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1504 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1505 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1506 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1507 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1508 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1509 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1510 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1511 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1512 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1513 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1514 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1515 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001516 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
1517 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
1518 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1519 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1520 {
1521 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1522 .name = "Capture Source",
1523 .info = ad198x_mux_enum_info,
1524 .get = ad198x_mux_enum_get,
1525 .put = ad198x_mux_enum_put,
1526 },
1527 /* identical with AD1983 */
1528 {
1529 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001530 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001531 .info = ad1983_spdif_route_info,
1532 .get = ad1983_spdif_route_get,
1533 .put = ad1983_spdif_route_put,
1534 },
1535 { } /* end */
1536};
1537
1538static struct hda_verb ad1981_init_verbs[] = {
1539 /* Front, HP, Mono; mute as default */
1540 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1541 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1542 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1543 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1544 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1545 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1546 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1547 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1548 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1549 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1550 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1551 /* Front, HP selectors; from Mix */
1552 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1553 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1554 /* Mono selector; from Mix */
1555 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1556 /* Mic Mixer; select Front Mic */
1557 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1558 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1559 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001560 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1561 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001562 /* Record selector: Front mic */
1563 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1564 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1565 /* SPDIF route: PCM */
1566 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1567 /* Front Pin */
1568 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1569 /* HP Pin */
1570 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1571 /* Mono Pin */
1572 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1573 /* Front & Rear Mic Pins */
1574 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1575 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1576 /* Line Pin */
1577 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1578 /* Digital Beep */
1579 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1580 /* Line-Out as Input: disabled */
1581 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1582 { } /* end */
1583};
1584
Takashi Iwaicb53c622007-08-10 17:21:45 +02001585#ifdef CONFIG_SND_HDA_POWER_SAVE
1586static struct hda_amp_list ad1981_loopbacks[] = {
1587 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1588 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1589 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1590 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1591 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1592 { } /* end */
1593};
1594#endif
1595
Takashi Iwai18a815d2006-03-01 19:54:39 +01001596/*
1597 * Patch for HP nx6320
1598 *
Tobin Davis18768992007-03-12 22:20:51 +01001599 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001600 * speaker output enabled _and_ mute-LED off.
1601 */
1602
1603#define AD1981_HP_EVENT 0x37
1604#define AD1981_MIC_EVENT 0x38
1605
1606static struct hda_verb ad1981_hp_init_verbs[] = {
1607 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1608 /* pin sensing on HP and Mic jacks */
1609 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1610 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1611 {}
1612};
1613
1614/* turn on/off EAPD (+ mute HP) as a master switch */
1615static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1616 struct snd_ctl_elem_value *ucontrol)
1617{
1618 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1619 struct ad198x_spec *spec = codec->spec;
1620
1621 if (! ad198x_eapd_put(kcontrol, ucontrol))
1622 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001623 /* change speaker pin appropriately */
1624 snd_hda_codec_write(codec, 0x05, 0,
1625 AC_VERB_SET_PIN_WIDGET_CONTROL,
1626 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001627 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001628 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1629 HDA_AMP_MUTE,
1630 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001631 return 1;
1632}
1633
1634/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02001635static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
1636 .ops = &snd_hda_bind_vol,
1637 .values = {
1638 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1639 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1640 0
1641 },
1642};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001643
1644/* mute internal speaker if HP is plugged */
1645static void ad1981_hp_automute(struct hda_codec *codec)
1646{
1647 unsigned int present;
1648
Takashi Iwaid56757a2009-11-18 08:00:14 +01001649 present = snd_hda_jack_detect(codec, 0x06);
Takashi Iwai47fd8302007-08-10 17:11:07 +02001650 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1651 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001652}
1653
1654/* toggle input of built-in and mic jack appropriately */
1655static void ad1981_hp_automic(struct hda_codec *codec)
1656{
1657 static struct hda_verb mic_jack_on[] = {
1658 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1659 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1660 {}
1661 };
1662 static struct hda_verb mic_jack_off[] = {
1663 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1664 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1665 {}
1666 };
1667 unsigned int present;
1668
Takashi Iwaid56757a2009-11-18 08:00:14 +01001669 present = snd_hda_jack_detect(codec, 0x08);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001670 if (present)
1671 snd_hda_sequence_write(codec, mic_jack_on);
1672 else
1673 snd_hda_sequence_write(codec, mic_jack_off);
1674}
1675
1676/* unsolicited event for HP jack sensing */
1677static void ad1981_hp_unsol_event(struct hda_codec *codec,
1678 unsigned int res)
1679{
1680 res >>= 26;
1681 switch (res) {
1682 case AD1981_HP_EVENT:
1683 ad1981_hp_automute(codec);
1684 break;
1685 case AD1981_MIC_EVENT:
1686 ad1981_hp_automic(codec);
1687 break;
1688 }
1689}
1690
1691static struct hda_input_mux ad1981_hp_capture_source = {
1692 .num_items = 3,
1693 .items = {
1694 { "Mic", 0x0 },
1695 { "Docking-Station", 0x1 },
1696 { "Mix", 0x2 },
1697 },
1698};
1699
1700static struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001701 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001702 {
1703 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001704 .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
Takashi Iwai18a815d2006-03-01 19:54:39 +01001705 .name = "Master Playback Switch",
1706 .info = ad198x_eapd_info,
1707 .get = ad198x_eapd_get,
1708 .put = ad1981_hp_master_sw_put,
1709 .private_value = 0x05,
1710 },
1711 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1712 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1713#if 0
1714 /* FIXME: analog mic/line loopback doesn't work with my tests...
1715 * (although recording is OK)
1716 */
1717 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1718 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1719 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1720 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1721 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1722 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1723 /* FIXME: does this laptop have analog CD connection? */
1724 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1725 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1726#endif
1727 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1728 HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
1729 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1730 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1731 {
1732 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1733 .name = "Capture Source",
1734 .info = ad198x_mux_enum_info,
1735 .get = ad198x_mux_enum_get,
1736 .put = ad198x_mux_enum_put,
1737 },
1738 { } /* end */
1739};
1740
1741/* initialize jack-sensing, too */
1742static int ad1981_hp_init(struct hda_codec *codec)
1743{
1744 ad198x_init(codec);
1745 ad1981_hp_automute(codec);
1746 ad1981_hp_automic(codec);
1747 return 0;
1748}
1749
Tobin Davis18768992007-03-12 22:20:51 +01001750/* configuration for Toshiba Laptops */
1751static struct hda_verb ad1981_toshiba_init_verbs[] = {
1752 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1753 /* pin sensing on HP and Mic jacks */
1754 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1755 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1756 {}
1757};
1758
1759static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
1760 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1761 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1762 { }
1763};
1764
Takashi Iwai01686c52006-04-18 12:54:11 +02001765/* configuration for Lenovo Thinkpad T60 */
1766static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
1767 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1768 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1769 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1770 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1771 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1772 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1773 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1774 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1775 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1776 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1777 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1778 {
1779 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1780 .name = "Capture Source",
1781 .info = ad198x_mux_enum_info,
1782 .get = ad198x_mux_enum_get,
1783 .put = ad198x_mux_enum_put,
1784 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001785 /* identical with AD1983 */
1786 {
1787 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1788 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1789 .info = ad1983_spdif_route_info,
1790 .get = ad1983_spdif_route_get,
1791 .put = ad1983_spdif_route_put,
1792 },
Takashi Iwai01686c52006-04-18 12:54:11 +02001793 { } /* end */
1794};
1795
1796static struct hda_input_mux ad1981_thinkpad_capture_source = {
1797 .num_items = 3,
1798 .items = {
1799 { "Mic", 0x0 },
1800 { "Mix", 0x2 },
1801 { "CD", 0x4 },
1802 },
1803};
1804
Takashi Iwai18a815d2006-03-01 19:54:39 +01001805/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001806enum {
1807 AD1981_BASIC,
1808 AD1981_HP,
1809 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001810 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001811 AD1981_MODELS
1812};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001813
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001814static const char *ad1981_models[AD1981_MODELS] = {
1815 [AD1981_HP] = "hp",
1816 [AD1981_THINKPAD] = "thinkpad",
1817 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001818 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001819};
1820
1821static struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001822 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001823 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001824 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001825 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001826 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c52006-04-18 12:54:11 +02001827 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001828 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001829 /* HP nx6320 (reversed SSID, H/W bug) */
1830 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001831 {}
1832};
1833
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001834static int patch_ad1981(struct hda_codec *codec)
1835{
1836 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001837 int err, board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001838
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001839 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001840 if (spec == NULL)
1841 return -ENOMEM;
1842
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001843 codec->spec = spec;
1844
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001845 err = snd_hda_attach_beep_device(codec, 0x10);
1846 if (err < 0) {
1847 ad198x_free(codec);
1848 return err;
1849 }
1850 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
1851
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001852 spec->multiout.max_channels = 2;
1853 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1854 spec->multiout.dac_nids = ad1981_dac_nids;
1855 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001856 spec->num_adc_nids = 1;
1857 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001858 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001859 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001860 spec->num_mixers = 1;
1861 spec->mixers[0] = ad1981_mixers;
1862 spec->num_init_verbs = 1;
1863 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001864 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001865#ifdef CONFIG_SND_HDA_POWER_SAVE
1866 spec->loopback.amplist = ad1981_loopbacks;
1867#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001868 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001869
1870 codec->patch_ops = ad198x_patch_ops;
1871
Takashi Iwai18a815d2006-03-01 19:54:39 +01001872 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001873 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1874 ad1981_models,
1875 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001876 switch (board_config) {
1877 case AD1981_HP:
1878 spec->mixers[0] = ad1981_hp_mixers;
1879 spec->num_init_verbs = 2;
1880 spec->init_verbs[1] = ad1981_hp_init_verbs;
1881 spec->multiout.dig_out_nid = 0;
1882 spec->input_mux = &ad1981_hp_capture_source;
1883
1884 codec->patch_ops.init = ad1981_hp_init;
1885 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
Daniel T Chen01f59662009-12-13 16:22:58 -05001886 /* set the upper-limit for mixer amp to 0dB for avoiding the
1887 * possible damage by overloading
1888 */
1889 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
1890 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
1891 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
1892 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
1893 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai18a815d2006-03-01 19:54:39 +01001894 break;
Takashi Iwai01686c52006-04-18 12:54:11 +02001895 case AD1981_THINKPAD:
1896 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c52006-04-18 12:54:11 +02001897 spec->input_mux = &ad1981_thinkpad_capture_source;
Daniel T Chenb8e80cf2010-03-30 13:29:28 -04001898 /* set the upper-limit for mixer amp to 0dB for avoiding the
1899 * possible damage by overloading
1900 */
1901 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
1902 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
1903 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
1904 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
1905 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai01686c52006-04-18 12:54:11 +02001906 break;
Tobin Davis18768992007-03-12 22:20:51 +01001907 case AD1981_TOSHIBA:
1908 spec->mixers[0] = ad1981_hp_mixers;
1909 spec->mixers[1] = ad1981_toshiba_mixers;
1910 spec->num_init_verbs = 2;
1911 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1912 spec->multiout.dig_out_nid = 0;
1913 spec->input_mux = &ad1981_hp_capture_source;
1914 codec->patch_ops.init = ad1981_hp_init;
1915 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1916 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001917 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01001918
1919 codec->no_trigger_sense = 1;
1920
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001921 return 0;
1922}
1923
1924
1925/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001926 * AD1988
1927 *
1928 * Output pins and routes
1929 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001930 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001931 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1932 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1933 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1934 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1935 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1936 * port-F 0x16 (mute) <- 0x2a <- 06
1937 * port-G 0x24 (mute) <- 0x27 <- 05
1938 * port-H 0x25 (mute) <- 0x28 <- 0a
1939 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1940 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001941 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1942 * (*) 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 +01001943 *
1944 * Input pins and routes
1945 *
1946 * pin boost mix input # / adc input #
1947 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1948 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1949 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1950 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1951 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1952 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1953 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1954 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1955 *
1956 *
1957 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001958 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001959 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001960 *
1961 * Inputs of Analog Mix (0x20)
1962 * 0:Port-B (front mic)
1963 * 1:Port-C/G/H (line-in)
1964 * 2:Port-A
1965 * 3:Port-D (line-in/2)
1966 * 4:Port-E/G/H (mic-in)
1967 * 5:Port-F (mic2-in)
1968 * 6:CD
1969 * 7:Beep
1970 *
1971 * ADC selection
1972 * 0:Port-A
1973 * 1:Port-B (front mic-in)
1974 * 2:Port-C (line-in)
1975 * 3:Port-F (mic2-in)
1976 * 4:Port-E (mic-in)
1977 * 5:CD
1978 * 6:Port-G
1979 * 7:Port-H
1980 * 8:Port-D (line-in/2)
1981 * 9:Mix
1982 *
1983 * Proposed pin assignments by the datasheet
1984 *
1985 * 6-stack
1986 * Port-A front headphone
1987 * B front mic-in
1988 * C rear line-in
1989 * D rear front-out
1990 * E rear mic-in
1991 * F rear surround
1992 * G rear CLFE
1993 * H rear side
1994 *
1995 * 3-stack
1996 * Port-A front headphone
1997 * B front mic
1998 * C rear line-in/surround
1999 * D rear front-out
2000 * E rear mic-in/CLFE
2001 *
2002 * laptop
2003 * Port-A headphone
2004 * B mic-in
2005 * C docking station
2006 * D internal speaker (with EAPD)
2007 * E/F quad mic array
2008 */
2009
2010
2011/* models */
2012enum {
2013 AD1988_6STACK,
2014 AD1988_6STACK_DIG,
2015 AD1988_3STACK,
2016 AD1988_3STACK_DIG,
2017 AD1988_LAPTOP,
2018 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01002019 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002020 AD1988_MODEL_LAST,
2021};
2022
Takashi Iwaid32410b12005-11-24 16:06:23 +01002023/* reivision id to check workarounds */
2024#define AD1988A_REV2 0x100200
2025
Takashi Iwai1a806f42006-07-03 15:58:16 +02002026#define is_rev2(codec) \
2027 ((codec)->vendor_id == 0x11d41988 && \
2028 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002029
2030/*
2031 * mixers
2032 */
2033
Takashi Iwaid32410b12005-11-24 16:06:23 +01002034static hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002035 0x04, 0x06, 0x05, 0x0a
2036};
2037
Takashi Iwaid32410b12005-11-24 16:06:23 +01002038static hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002039 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002040};
2041
2042/* for AD1988A revision-2, DAC2-4 are swapped */
2043static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
2044 0x04, 0x05, 0x0a, 0x06
2045};
2046
2047static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002048 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002049};
2050
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002051static hda_nid_t ad1988_adc_nids[3] = {
2052 0x08, 0x09, 0x0f
2053};
2054
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002055static hda_nid_t ad1988_capsrc_nids[3] = {
2056 0x0c, 0x0d, 0x0e
2057};
2058
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002059#define AD1988_SPDIF_OUT 0x02
2060#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002061#define AD1988_SPDIF_IN 0x07
2062
Takashi Iwai3a08e302009-02-13 11:37:08 +01002063static hda_nid_t ad1989b_slave_dig_outs[] = {
2064 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002065};
2066
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002067static struct hda_input_mux ad1988_6stack_capture_source = {
2068 .num_items = 5,
2069 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002070 { "Front Mic", 0x1 }, /* port-B */
2071 { "Line", 0x2 }, /* port-C */
2072 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002073 { "CD", 0x5 },
2074 { "Mix", 0x9 },
2075 },
2076};
2077
2078static struct hda_input_mux ad1988_laptop_capture_source = {
2079 .num_items = 3,
2080 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002081 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002082 { "CD", 0x5 },
2083 { "Mix", 0x9 },
2084 },
2085};
2086
2087/*
2088 */
2089static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
2090 struct snd_ctl_elem_info *uinfo)
2091{
2092 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2093 struct ad198x_spec *spec = codec->spec;
2094 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
2095 spec->num_channel_mode);
2096}
2097
2098static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
2099 struct snd_ctl_elem_value *ucontrol)
2100{
2101 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2102 struct ad198x_spec *spec = codec->spec;
2103 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
2104 spec->num_channel_mode, spec->multiout.max_channels);
2105}
2106
2107static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
2108 struct snd_ctl_elem_value *ucontrol)
2109{
2110 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2111 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002112 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
2113 spec->num_channel_mode,
2114 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02002115 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02002116 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002117 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002118}
2119
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002120/* 6-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002121static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002122 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2123 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
2124 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2125 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
2126 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002127 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002128};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002129
Takashi Iwaid32410b12005-11-24 16:06:23 +01002130static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
2131 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2132 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2133 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2134 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2135 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002136 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002137};
2138
2139static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002140 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2141 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2142 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2143 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2144 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2145 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2146 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2147
2148 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2149 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2150 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2151 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2152 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2153 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2154 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2155 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2156
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002157 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002158 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2159
2160 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2161 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2162
2163 { } /* end */
2164};
2165
2166/* 3-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002167static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002168 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002169 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002170 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2171 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002172 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002173};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002174
Takashi Iwaid32410b12005-11-24 16:06:23 +01002175static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
2176 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002177 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2178 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2179 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002180 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002181};
2182
2183static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002184 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002185 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2186 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2187 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002188 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2189 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2190
2191 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2192 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2193 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2194 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2195 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2196 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2197 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2198 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2199
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002200 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002201 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2202
2203 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2204 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2205 {
2206 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2207 .name = "Channel Mode",
2208 .info = ad198x_ch_mode_info,
2209 .get = ad198x_ch_mode_get,
2210 .put = ad198x_ch_mode_put,
2211 },
2212
2213 { } /* end */
2214};
2215
2216/* laptop mode */
2217static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
2218 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2219 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2220 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2221
2222 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2223 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2224 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2225 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2226 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2227 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2228
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002229 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002230 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2231
2232 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2233
2234 {
2235 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2236 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002237 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01002238 .info = ad198x_eapd_info,
2239 .get = ad198x_eapd_get,
2240 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01002241 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002242 },
2243
2244 { } /* end */
2245};
2246
2247/* capture */
2248static struct snd_kcontrol_new ad1988_capture_mixers[] = {
2249 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2250 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2251 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2252 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2253 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2254 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2255 {
2256 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2257 /* The multiple "Capture Source" controls confuse alsamixer
2258 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002259 */
2260 /* .name = "Capture Source", */
2261 .name = "Input Source",
2262 .count = 3,
2263 .info = ad198x_mux_enum_info,
2264 .get = ad198x_mux_enum_get,
2265 .put = ad198x_mux_enum_put,
2266 },
2267 { } /* end */
2268};
2269
2270static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2271 struct snd_ctl_elem_info *uinfo)
2272{
2273 static char *texts[] = {
2274 "PCM", "ADC1", "ADC2", "ADC3"
2275 };
2276 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2277 uinfo->count = 1;
2278 uinfo->value.enumerated.items = 4;
2279 if (uinfo->value.enumerated.item >= 4)
2280 uinfo->value.enumerated.item = 3;
2281 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2282 return 0;
2283}
2284
2285static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2286 struct snd_ctl_elem_value *ucontrol)
2287{
2288 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2289 unsigned int sel;
2290
Takashi Iwaibddcf542007-07-24 18:04:05 +02002291 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2292 AC_AMP_GET_INPUT);
2293 if (!(sel & 0x80))
2294 ucontrol->value.enumerated.item[0] = 0;
2295 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002296 sel = snd_hda_codec_read(codec, 0x0b, 0,
2297 AC_VERB_GET_CONNECT_SEL, 0);
2298 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002299 sel++;
2300 else
2301 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002302 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002303 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002304 return 0;
2305}
2306
2307static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2308 struct snd_ctl_elem_value *ucontrol)
2309{
2310 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002311 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002312 int change;
2313
Takashi Iwai35b26722007-05-05 12:17:17 +02002314 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002315 if (val > 3)
2316 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002317 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002318 sel = snd_hda_codec_read(codec, 0x1d, 0,
2319 AC_VERB_GET_AMP_GAIN_MUTE,
2320 AC_AMP_GET_INPUT);
2321 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002322 if (change) {
2323 snd_hda_codec_write_cache(codec, 0x1d, 0,
2324 AC_VERB_SET_AMP_GAIN_MUTE,
2325 AMP_IN_UNMUTE(0));
2326 snd_hda_codec_write_cache(codec, 0x1d, 0,
2327 AC_VERB_SET_AMP_GAIN_MUTE,
2328 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002329 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002330 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002331 sel = snd_hda_codec_read(codec, 0x1d, 0,
2332 AC_VERB_GET_AMP_GAIN_MUTE,
2333 AC_AMP_GET_INPUT | 0x01);
2334 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002335 if (change) {
2336 snd_hda_codec_write_cache(codec, 0x1d, 0,
2337 AC_VERB_SET_AMP_GAIN_MUTE,
2338 AMP_IN_MUTE(0));
2339 snd_hda_codec_write_cache(codec, 0x1d, 0,
2340 AC_VERB_SET_AMP_GAIN_MUTE,
2341 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002342 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002343 sel = snd_hda_codec_read(codec, 0x0b, 0,
2344 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2345 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002346 if (change)
2347 snd_hda_codec_write_cache(codec, 0x0b, 0,
2348 AC_VERB_SET_CONNECT_SEL,
2349 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002350 }
2351 return change;
2352}
2353
2354static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
2355 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2356 {
2357 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2358 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002359 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002360 .info = ad1988_spdif_playback_source_info,
2361 .get = ad1988_spdif_playback_source_get,
2362 .put = ad1988_spdif_playback_source_put,
2363 },
2364 { } /* end */
2365};
2366
2367static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
2368 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2369 { } /* end */
2370};
2371
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002372static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
2373 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002374 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002375 { } /* end */
2376};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002377
2378/*
2379 * initialization verbs
2380 */
2381
2382/*
2383 * for 6-stack (+dig)
2384 */
2385static struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002386 /* Front, Surround, CLFE, side DAC; unmute as default */
2387 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2388 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2389 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2390 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002391 /* Port-A front headphon path */
2392 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2393 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2394 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2395 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2396 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2397 /* Port-D line-out path */
2398 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2399 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2400 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2401 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2402 /* Port-F surround path */
2403 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2404 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2405 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2406 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2407 /* Port-G CLFE path */
2408 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2409 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2410 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2411 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2412 /* Port-H side path */
2413 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2414 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2415 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2416 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2417 /* Mono out path */
2418 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2419 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2420 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2421 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2422 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2423 /* Port-B front mic-in path */
2424 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2425 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2426 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2427 /* Port-C line-in path */
2428 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2429 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2430 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2431 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2432 /* Port-E mic-in path */
2433 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2434 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2435 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2436 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002437 /* Analog CD Input */
2438 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002439 /* Analog Mix output amp */
2440 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002441
2442 { }
2443};
2444
2445static struct hda_verb ad1988_capture_init_verbs[] = {
2446 /* mute analog mix */
2447 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2448 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2449 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2450 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2451 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2452 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2453 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2454 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2455 /* select ADCs - front-mic */
2456 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2457 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2458 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002459
2460 { }
2461};
2462
2463static struct hda_verb ad1988_spdif_init_verbs[] = {
2464 /* SPDIF out sel */
2465 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2466 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2467 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002468 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002469 /* SPDIF out pin */
2470 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002471
2472 { }
2473};
2474
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002475static struct hda_verb ad1988_spdif_in_init_verbs[] = {
2476 /* unmute SPDIF input pin */
2477 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2478 { }
2479};
2480
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002481/* AD1989 has no ADC -> SPDIF route */
2482static struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002483 /* SPDIF-1 out pin */
2484 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002485 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002486 /* SPDIF-2/HDMI out pin */
2487 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2488 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002489 { }
2490};
2491
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002492/*
2493 * verbs for 3stack (+dig)
2494 */
2495static struct hda_verb ad1988_3stack_ch2_init[] = {
2496 /* set port-C to line-in */
2497 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2498 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2499 /* set port-E to mic-in */
2500 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2501 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2502 { } /* end */
2503};
2504
2505static struct hda_verb ad1988_3stack_ch6_init[] = {
2506 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002507 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002508 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002509 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002510 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002511 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002512 { } /* end */
2513};
2514
2515static struct hda_channel_mode ad1988_3stack_modes[2] = {
2516 { 2, ad1988_3stack_ch2_init },
2517 { 6, ad1988_3stack_ch6_init },
2518};
2519
2520static struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002521 /* Front, Surround, CLFE, side DAC; unmute as default */
2522 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2523 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2524 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2525 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002526 /* Port-A front headphon path */
2527 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2528 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2529 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2530 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2531 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2532 /* Port-D line-out path */
2533 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2534 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2535 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2536 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2537 /* Mono out path */
2538 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2539 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2540 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2541 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2542 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2543 /* Port-B front mic-in path */
2544 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2545 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2546 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002547 /* Port-C line-in/surround path - 6ch mode as default */
2548 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2549 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002550 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002551 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002552 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002553 /* Port-E mic-in/CLFE path - 6ch mode as default */
2554 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2555 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002556 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002557 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002558 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2559 /* mute analog mix */
2560 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2561 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2562 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2563 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2564 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2565 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2566 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2567 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2568 /* select ADCs - front-mic */
2569 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2570 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2571 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002572 /* Analog Mix output amp */
2573 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002574 { }
2575};
2576
2577/*
2578 * verbs for laptop mode (+dig)
2579 */
2580static struct hda_verb ad1988_laptop_hp_on[] = {
2581 /* unmute port-A and mute port-D */
2582 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2583 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2584 { } /* end */
2585};
2586static struct hda_verb ad1988_laptop_hp_off[] = {
2587 /* mute port-A and unmute port-D */
2588 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2589 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2590 { } /* end */
2591};
2592
2593#define AD1988_HP_EVENT 0x01
2594
2595static struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002596 /* Front, Surround, CLFE, side DAC; unmute as default */
2597 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2598 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2599 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2600 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002601 /* Port-A front headphon path */
2602 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2603 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2604 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2605 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2606 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2607 /* unsolicited event for pin-sense */
2608 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2609 /* Port-D line-out path + EAPD */
2610 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2611 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2612 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2613 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2614 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2615 /* Mono out path */
2616 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2617 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2618 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2619 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2620 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2621 /* Port-B mic-in path */
2622 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2623 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2624 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2625 /* Port-C docking station - try to output */
2626 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2627 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2628 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2629 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2630 /* mute analog mix */
2631 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2632 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2633 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2634 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2635 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2636 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2637 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2638 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2639 /* select ADCs - mic */
2640 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2641 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2642 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002643 /* Analog Mix output amp */
2644 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002645 { }
2646};
2647
2648static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2649{
2650 if ((res >> 26) != AD1988_HP_EVENT)
2651 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01002652 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002653 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2654 else
2655 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2656}
2657
Takashi Iwaicb53c622007-08-10 17:21:45 +02002658#ifdef CONFIG_SND_HDA_POWER_SAVE
2659static struct hda_amp_list ad1988_loopbacks[] = {
2660 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2661 { 0x20, HDA_INPUT, 1 }, /* Line */
2662 { 0x20, HDA_INPUT, 4 }, /* Mic */
2663 { 0x20, HDA_INPUT, 6 }, /* CD */
2664 { } /* end */
2665};
2666#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002667
2668/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002669 * Automatic parse of I/O pins from the BIOS configuration
2670 */
2671
Takashi Iwaid32410b12005-11-24 16:06:23 +01002672enum {
2673 AD_CTL_WIDGET_VOL,
2674 AD_CTL_WIDGET_MUTE,
2675 AD_CTL_BIND_MUTE,
2676};
2677static struct snd_kcontrol_new ad1988_control_templates[] = {
2678 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2679 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2680 HDA_BIND_MUTE(NULL, 0, 0, 0),
2681};
2682
2683/* add dynamic controls */
2684static int add_control(struct ad198x_spec *spec, int type, const char *name,
2685 unsigned long val)
2686{
2687 struct snd_kcontrol_new *knew;
2688
Takashi Iwai603c4012008-07-30 15:01:44 +02002689 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2690 knew = snd_array_new(&spec->kctls);
2691 if (!knew)
2692 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002693 *knew = ad1988_control_templates[type];
2694 knew->name = kstrdup(name, GFP_KERNEL);
2695 if (! knew->name)
2696 return -ENOMEM;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +01002697 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01002698 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002699 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002700 return 0;
2701}
2702
2703#define AD1988_PIN_CD_NID 0x18
2704#define AD1988_PIN_BEEP_NID 0x10
2705
2706static hda_nid_t ad1988_mixer_nids[8] = {
2707 /* A B C D E F G H */
2708 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2709};
2710
2711static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2712{
2713 static hda_nid_t idx_to_dac[8] = {
2714 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002715 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002716 };
2717 static hda_nid_t idx_to_dac_rev2[8] = {
2718 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002719 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002720 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002721 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002722 return idx_to_dac_rev2[idx];
2723 else
2724 return idx_to_dac[idx];
2725}
2726
2727static hda_nid_t ad1988_boost_nids[8] = {
2728 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2729};
2730
2731static int ad1988_pin_idx(hda_nid_t nid)
2732{
2733 static hda_nid_t ad1988_io_pins[8] = {
2734 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2735 };
2736 int i;
2737 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2738 if (ad1988_io_pins[i] == nid)
2739 return i;
2740 return 0; /* should be -1 */
2741}
2742
2743static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2744{
2745 static int loopback_idx[8] = {
2746 2, 0, 1, 3, 4, 5, 1, 4
2747 };
2748 switch (nid) {
2749 case AD1988_PIN_CD_NID:
2750 return 6;
2751 default:
2752 return loopback_idx[ad1988_pin_idx(nid)];
2753 }
2754}
2755
2756static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2757{
2758 static int adc_idx[8] = {
2759 0, 1, 2, 8, 4, 3, 6, 7
2760 };
2761 switch (nid) {
2762 case AD1988_PIN_CD_NID:
2763 return 5;
2764 default:
2765 return adc_idx[ad1988_pin_idx(nid)];
2766 }
2767}
2768
2769/* fill in the dac_nids table from the parsed pin configuration */
2770static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2771 const struct auto_pin_cfg *cfg)
2772{
2773 struct ad198x_spec *spec = codec->spec;
2774 int i, idx;
2775
2776 spec->multiout.dac_nids = spec->private_dac_nids;
2777
2778 /* check the pins hardwired to audio widget */
2779 for (i = 0; i < cfg->line_outs; i++) {
2780 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2781 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2782 }
2783 spec->multiout.num_dacs = cfg->line_outs;
2784 return 0;
2785}
2786
2787/* add playback controls from the parsed DAC table */
2788static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2789 const struct auto_pin_cfg *cfg)
2790{
2791 char name[32];
2792 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2793 hda_nid_t nid;
2794 int i, err;
2795
2796 for (i = 0; i < cfg->line_outs; i++) {
2797 hda_nid_t dac = spec->multiout.dac_nids[i];
2798 if (! dac)
2799 continue;
2800 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2801 if (i == 2) {
2802 /* Center/LFE */
2803 err = add_control(spec, AD_CTL_WIDGET_VOL,
2804 "Center Playback Volume",
2805 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2806 if (err < 0)
2807 return err;
2808 err = add_control(spec, AD_CTL_WIDGET_VOL,
2809 "LFE Playback Volume",
2810 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2811 if (err < 0)
2812 return err;
2813 err = add_control(spec, AD_CTL_BIND_MUTE,
2814 "Center Playback Switch",
2815 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2816 if (err < 0)
2817 return err;
2818 err = add_control(spec, AD_CTL_BIND_MUTE,
2819 "LFE Playback Switch",
2820 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2821 if (err < 0)
2822 return err;
2823 } else {
2824 sprintf(name, "%s Playback Volume", chname[i]);
2825 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2826 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2827 if (err < 0)
2828 return err;
2829 sprintf(name, "%s Playback Switch", chname[i]);
2830 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2831 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2832 if (err < 0)
2833 return err;
2834 }
2835 }
2836 return 0;
2837}
2838
2839/* add playback controls for speaker and HP outputs */
2840static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2841 const char *pfx)
2842{
2843 struct ad198x_spec *spec = codec->spec;
2844 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02002845 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002846 char name[32];
2847
2848 if (! pin)
2849 return 0;
2850
2851 idx = ad1988_pin_idx(pin);
2852 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02002853 /* check whether the corresponding DAC was already taken */
2854 for (i = 0; i < spec->autocfg.line_outs; i++) {
2855 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2856 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
2857 if (dac == nid)
2858 break;
2859 }
2860 if (i >= spec->autocfg.line_outs) {
2861 /* specify the DAC as the extra output */
2862 if (!spec->multiout.hp_nid)
2863 spec->multiout.hp_nid = nid;
2864 else
2865 spec->multiout.extra_out_nid[0] = nid;
2866 /* control HP volume/switch on the output mixer amp */
2867 sprintf(name, "%s Playback Volume", pfx);
2868 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2869 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2870 if (err < 0)
2871 return err;
2872 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01002873 nid = ad1988_mixer_nids[idx];
2874 sprintf(name, "%s Playback Switch", pfx);
2875 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2876 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2877 return err;
2878 return 0;
2879}
2880
2881/* create input playback/capture controls for the given pin */
2882static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2883 const char *ctlname, int boost)
2884{
2885 char name[32];
2886 int err, idx;
2887
2888 sprintf(name, "%s Playback Volume", ctlname);
2889 idx = ad1988_pin_to_loopback_idx(pin);
2890 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2891 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2892 return err;
2893 sprintf(name, "%s Playback Switch", ctlname);
2894 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2895 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2896 return err;
2897 if (boost) {
2898 hda_nid_t bnid;
2899 idx = ad1988_pin_idx(pin);
2900 bnid = ad1988_boost_nids[idx];
2901 if (bnid) {
2902 sprintf(name, "%s Boost", ctlname);
2903 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2904 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2905
2906 }
2907 }
2908 return 0;
2909}
2910
2911/* create playback/capture controls for input pins */
2912static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2913 const struct auto_pin_cfg *cfg)
2914{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002915 struct hda_input_mux *imux = &spec->private_imux;
2916 int i, err;
2917
2918 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002919 err = new_analog_input(spec, cfg->input_pins[i],
2920 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002921 i <= AUTO_PIN_FRONT_MIC);
2922 if (err < 0)
2923 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002924 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002925 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2926 imux->num_items++;
2927 }
2928 imux->items[imux->num_items].label = "Mix";
2929 imux->items[imux->num_items].index = 9;
2930 imux->num_items++;
2931
2932 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2933 "Analog Mix Playback Volume",
2934 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2935 return err;
2936 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2937 "Analog Mix Playback Switch",
2938 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2939 return err;
2940
2941 return 0;
2942}
2943
2944static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2945 hda_nid_t nid, int pin_type,
2946 int dac_idx)
2947{
2948 /* set as output */
2949 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2950 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2951 switch (nid) {
2952 case 0x11: /* port-A - DAC 04 */
2953 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2954 break;
2955 case 0x14: /* port-B - DAC 06 */
2956 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2957 break;
2958 case 0x15: /* port-C - DAC 05 */
2959 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2960 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002961 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002962 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2963 break;
2964 case 0x13: /* mono - DAC 04 */
2965 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2966 break;
2967 }
2968}
2969
2970static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2971{
2972 struct ad198x_spec *spec = codec->spec;
2973 int i;
2974
2975 for (i = 0; i < spec->autocfg.line_outs; i++) {
2976 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2977 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2978 }
2979}
2980
2981static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2982{
2983 struct ad198x_spec *spec = codec->spec;
2984 hda_nid_t pin;
2985
Takashi Iwai82bc9552006-03-21 11:24:42 +01002986 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002987 if (pin) /* connect to front */
2988 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002989 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002990 if (pin) /* connect to front */
2991 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2992}
2993
2994static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2995{
2996 struct ad198x_spec *spec = codec->spec;
2997 int i, idx;
2998
2999 for (i = 0; i < AUTO_PIN_LAST; i++) {
3000 hda_nid_t nid = spec->autocfg.input_pins[i];
3001 if (! nid)
3002 continue;
3003 switch (nid) {
3004 case 0x15: /* port-C */
3005 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
3006 break;
3007 case 0x17: /* port-E */
3008 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
3009 break;
3010 }
3011 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
3012 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
3013 if (nid != AD1988_PIN_CD_NID)
3014 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
3015 AMP_OUT_MUTE);
3016 idx = ad1988_pin_idx(nid);
3017 if (ad1988_boost_nids[idx])
3018 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
3019 AC_VERB_SET_AMP_GAIN_MUTE,
3020 AMP_OUT_ZERO);
3021 }
3022}
3023
3024/* parse the BIOS configuration and set up the alc_spec */
3025/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
3026static int ad1988_parse_auto_config(struct hda_codec *codec)
3027{
3028 struct ad198x_spec *spec = codec->spec;
3029 int err;
3030
Kailang Yangdf694da2005-12-05 19:42:22 +01003031 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003032 return err;
3033 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
3034 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01003035 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003036 return 0; /* can't find valid BIOS pin config */
3037 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01003038 (err = ad1988_auto_create_extra_out(codec,
3039 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003040 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003041 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003042 "Headphone")) < 0 ||
3043 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
3044 return err;
3045
3046 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3047
Takashi Iwai0852d7a2009-02-11 11:35:15 +01003048 if (spec->autocfg.dig_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003049 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3050 if (spec->autocfg.dig_in_pin)
3051 spec->dig_in_nid = AD1988_SPDIF_IN;
3052
Takashi Iwai603c4012008-07-30 15:01:44 +02003053 if (spec->kctls.list)
3054 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003055
3056 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
3057
3058 spec->input_mux = &spec->private_imux;
3059
3060 return 1;
3061}
3062
3063/* init callback for auto-configuration model -- overriding the default init */
3064static int ad1988_auto_init(struct hda_codec *codec)
3065{
3066 ad198x_init(codec);
3067 ad1988_auto_init_multi_out(codec);
3068 ad1988_auto_init_extra_out(codec);
3069 ad1988_auto_init_analog_input(codec);
3070 return 0;
3071}
3072
3073
3074/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003075 */
3076
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003077static const char *ad1988_models[AD1988_MODEL_LAST] = {
3078 [AD1988_6STACK] = "6stack",
3079 [AD1988_6STACK_DIG] = "6stack-dig",
3080 [AD1988_3STACK] = "3stack",
3081 [AD1988_3STACK_DIG] = "3stack-dig",
3082 [AD1988_LAPTOP] = "laptop",
3083 [AD1988_LAPTOP_DIG] = "laptop-dig",
3084 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003085};
3086
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003087static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01003088 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01003089 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02003090 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07003091 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003092 {}
3093};
3094
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003095static int patch_ad1988(struct hda_codec *codec)
3096{
3097 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003098 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003099
3100 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3101 if (spec == NULL)
3102 return -ENOMEM;
3103
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003104 codec->spec = spec;
3105
Takashi Iwai1a806f42006-07-03 15:58:16 +02003106 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003107 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
3108
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003109 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003110 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003111 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02003112 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3113 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003114 board_config = AD1988_AUTO;
3115 }
3116
3117 if (board_config == AD1988_AUTO) {
3118 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003119 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003120 if (err < 0) {
3121 ad198x_free(codec);
3122 return err;
3123 } else if (! err) {
3124 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
3125 board_config = AD1988_6STACK;
3126 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003127 }
3128
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003129 err = snd_hda_attach_beep_device(codec, 0x10);
3130 if (err < 0) {
3131 ad198x_free(codec);
3132 return err;
3133 }
3134 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3135
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003136 switch (board_config) {
3137 case AD1988_6STACK:
3138 case AD1988_6STACK_DIG:
3139 spec->multiout.max_channels = 8;
3140 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003141 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003142 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3143 else
3144 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003145 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003146 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003147 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003148 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3149 else
3150 spec->mixers[0] = ad1988_6stack_mixers1;
3151 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003152 spec->num_init_verbs = 1;
3153 spec->init_verbs[0] = ad1988_6stack_init_verbs;
3154 if (board_config == AD1988_6STACK_DIG) {
3155 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3156 spec->dig_in_nid = AD1988_SPDIF_IN;
3157 }
3158 break;
3159 case AD1988_3STACK:
3160 case AD1988_3STACK_DIG:
3161 spec->multiout.max_channels = 6;
3162 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003163 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003164 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3165 else
3166 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003167 spec->input_mux = &ad1988_6stack_capture_source;
3168 spec->channel_mode = ad1988_3stack_modes;
3169 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003170 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003171 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003172 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3173 else
3174 spec->mixers[0] = ad1988_3stack_mixers1;
3175 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003176 spec->num_init_verbs = 1;
3177 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3178 if (board_config == AD1988_3STACK_DIG)
3179 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3180 break;
3181 case AD1988_LAPTOP:
3182 case AD1988_LAPTOP_DIG:
3183 spec->multiout.max_channels = 2;
3184 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003185 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003186 spec->input_mux = &ad1988_laptop_capture_source;
3187 spec->num_mixers = 1;
3188 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01003189 spec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003190 spec->num_init_verbs = 1;
3191 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3192 if (board_config == AD1988_LAPTOP_DIG)
3193 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3194 break;
3195 }
3196
Takashi Iwaid32410b12005-11-24 16:06:23 +01003197 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3198 spec->adc_nids = ad1988_adc_nids;
3199 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003200 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3201 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3202 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003203 if (codec->vendor_id >= 0x11d4989a) {
3204 spec->mixers[spec->num_mixers++] =
3205 ad1989_spdif_out_mixers;
3206 spec->init_verbs[spec->num_init_verbs++] =
3207 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003208 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003209 } else {
3210 spec->mixers[spec->num_mixers++] =
3211 ad1988_spdif_out_mixers;
3212 spec->init_verbs[spec->num_init_verbs++] =
3213 ad1988_spdif_init_verbs;
3214 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003215 }
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003216 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003217 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003218 spec->init_verbs[spec->num_init_verbs++] =
3219 ad1988_spdif_in_init_verbs;
3220 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003221
3222 codec->patch_ops = ad198x_patch_ops;
3223 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003224 case AD1988_AUTO:
3225 codec->patch_ops.init = ad1988_auto_init;
3226 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003227 case AD1988_LAPTOP:
3228 case AD1988_LAPTOP_DIG:
3229 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3230 break;
3231 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003232#ifdef CONFIG_SND_HDA_POWER_SAVE
3233 spec->loopback.amplist = ad1988_loopbacks;
3234#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003235 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003236
Takashi Iwai729d55b2009-12-25 22:49:01 +01003237 codec->no_trigger_sense = 1;
3238
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003239 return 0;
3240}
3241
3242
3243/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003244 * AD1884 / AD1984
3245 *
3246 * port-B - front line/mic-in
3247 * port-E - aux in/out
3248 * port-F - aux in/out
3249 * port-C - rear line/mic-in
3250 * port-D - rear line/hp-out
3251 * port-A - front line/hp-out
3252 *
3253 * AD1984 = AD1884 + two digital mic-ins
3254 *
3255 * FIXME:
3256 * For simplicity, we share the single DAC for both HP and line-outs
3257 * right now. The inidividual playbacks could be easily implemented,
3258 * but no build-up framework is given, so far.
3259 */
3260
3261static hda_nid_t ad1884_dac_nids[1] = {
3262 0x04,
3263};
3264
3265static hda_nid_t ad1884_adc_nids[2] = {
3266 0x08, 0x09,
3267};
3268
3269static hda_nid_t ad1884_capsrc_nids[2] = {
3270 0x0c, 0x0d,
3271};
3272
3273#define AD1884_SPDIF_OUT 0x02
3274
3275static struct hda_input_mux ad1884_capture_source = {
3276 .num_items = 4,
3277 .items = {
3278 { "Front Mic", 0x0 },
3279 { "Mic", 0x1 },
3280 { "CD", 0x2 },
3281 { "Mix", 0x3 },
3282 },
3283};
3284
3285static struct snd_kcontrol_new ad1884_base_mixers[] = {
3286 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3287 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3288 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3289 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3290 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3291 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3292 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3293 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3294 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3295 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3296 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3297 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003298 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
3299 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3300 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3301 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3302 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3303 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3304 {
3305 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3306 /* The multiple "Capture Source" controls confuse alsamixer
3307 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003308 */
3309 /* .name = "Capture Source", */
3310 .name = "Input Source",
3311 .count = 2,
3312 .info = ad198x_mux_enum_info,
3313 .get = ad198x_mux_enum_get,
3314 .put = ad198x_mux_enum_put,
3315 },
3316 /* SPDIF controls */
3317 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3318 {
3319 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3320 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3321 /* identical with ad1983 */
3322 .info = ad1983_spdif_route_info,
3323 .get = ad1983_spdif_route_get,
3324 .put = ad1983_spdif_route_put,
3325 },
3326 { } /* end */
3327};
3328
3329static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3330 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3331 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3332 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003333 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003334 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003335 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003336 { } /* end */
3337};
3338
3339/*
3340 * initialization verbs
3341 */
3342static struct hda_verb ad1884_init_verbs[] = {
3343 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003344 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3345 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003346 /* Port-A (HP) mixer */
3347 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3348 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3349 /* Port-A pin */
3350 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3351 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3352 /* HP selector - select DAC2 */
3353 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3354 /* Port-D (Line-out) mixer */
3355 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3356 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3357 /* Port-D pin */
3358 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3359 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3360 /* Mono-out mixer */
3361 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3362 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3363 /* Mono-out pin */
3364 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3365 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3366 /* Mono selector */
3367 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3368 /* Port-B (front mic) pin */
3369 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003370 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003371 /* Port-C (rear mic) pin */
3372 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003373 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003374 /* Analog mixer; mute as default */
3375 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3376 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3377 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3378 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3379 /* Analog Mix output amp */
3380 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3381 /* SPDIF output selector */
3382 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3383 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3384 { } /* end */
3385};
3386
Takashi Iwaicb53c622007-08-10 17:21:45 +02003387#ifdef CONFIG_SND_HDA_POWER_SAVE
3388static struct hda_amp_list ad1884_loopbacks[] = {
3389 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3390 { 0x20, HDA_INPUT, 1 }, /* Mic */
3391 { 0x20, HDA_INPUT, 2 }, /* CD */
3392 { 0x20, HDA_INPUT, 4 }, /* Docking */
3393 { } /* end */
3394};
3395#endif
3396
Takashi Iwai2134ea42008-01-10 16:53:55 +01003397static const char *ad1884_slave_vols[] = {
3398 "PCM Playback Volume",
3399 "Mic Playback Volume",
3400 "Mono Playback Volume",
3401 "Front Mic Playback Volume",
3402 "Mic Playback Volume",
3403 "CD Playback Volume",
3404 "Internal Mic Playback Volume",
Akinobu Mitabca68462009-04-06 18:42:42 +09003405 "Docking Mic Playback Volume",
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003406 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003407 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003408 NULL
3409};
3410
Takashi Iwai2bac6472007-05-18 18:21:41 +02003411static int patch_ad1884(struct hda_codec *codec)
3412{
3413 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003414 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003415
3416 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3417 if (spec == NULL)
3418 return -ENOMEM;
3419
Takashi Iwai2bac6472007-05-18 18:21:41 +02003420 codec->spec = spec;
3421
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003422 err = snd_hda_attach_beep_device(codec, 0x10);
3423 if (err < 0) {
3424 ad198x_free(codec);
3425 return err;
3426 }
3427 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3428
Takashi Iwai2bac6472007-05-18 18:21:41 +02003429 spec->multiout.max_channels = 2;
3430 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3431 spec->multiout.dac_nids = ad1884_dac_nids;
3432 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3433 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3434 spec->adc_nids = ad1884_adc_nids;
3435 spec->capsrc_nids = ad1884_capsrc_nids;
3436 spec->input_mux = &ad1884_capture_source;
3437 spec->num_mixers = 1;
3438 spec->mixers[0] = ad1884_base_mixers;
3439 spec->num_init_verbs = 1;
3440 spec->init_verbs[0] = ad1884_init_verbs;
3441 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003442#ifdef CONFIG_SND_HDA_POWER_SAVE
3443 spec->loopback.amplist = ad1884_loopbacks;
3444#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003445 spec->vmaster_nid = 0x04;
3446 /* we need to cover all playback volumes */
3447 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003448
3449 codec->patch_ops = ad198x_patch_ops;
3450
Takashi Iwai729d55b2009-12-25 22:49:01 +01003451 codec->no_trigger_sense = 1;
3452
Takashi Iwai2bac6472007-05-18 18:21:41 +02003453 return 0;
3454}
3455
3456/*
3457 * Lenovo Thinkpad T61/X61
3458 */
3459static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003460 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003461 .items = {
3462 { "Mic", 0x0 },
3463 { "Internal Mic", 0x1 },
3464 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003465 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003466 },
3467};
3468
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003469
3470/*
3471 * Dell Precision T3400
3472 */
3473static struct hda_input_mux ad1984_dell_desktop_capture_source = {
3474 .num_items = 3,
3475 .items = {
3476 { "Front Mic", 0x0 },
3477 { "Line-In", 0x1 },
3478 { "Mix", 0x3 },
3479 },
3480};
3481
3482
Takashi Iwai2bac6472007-05-18 18:21:41 +02003483static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3484 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3485 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3486 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3487 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3488 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3489 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3490 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3491 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003492 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3493 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003494 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3495 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003496 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3497 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003498 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003499 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3500 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3501 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3502 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3503 {
3504 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3505 /* The multiple "Capture Source" controls confuse alsamixer
3506 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003507 */
3508 /* .name = "Capture Source", */
3509 .name = "Input Source",
3510 .count = 2,
3511 .info = ad198x_mux_enum_info,
3512 .get = ad198x_mux_enum_get,
3513 .put = ad198x_mux_enum_put,
3514 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003515 /* SPDIF controls */
3516 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3517 {
3518 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3519 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3520 /* identical with ad1983 */
3521 .info = ad1983_spdif_route_info,
3522 .get = ad1983_spdif_route_get,
3523 .put = ad1983_spdif_route_put,
3524 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003525 { } /* end */
3526};
3527
3528/* additional verbs */
3529static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3530 /* Port-E (docking station mic) pin */
3531 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3532 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3533 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003534 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003535 /* Analog PC Beeper - allow firmware/ACPI beeps */
3536 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003537 /* Analog mixer - docking mic; mute as default */
3538 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003539 /* enable EAPD bit */
3540 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003541 { } /* end */
3542};
3543
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003544/*
3545 * Dell Precision T3400
3546 */
3547static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
3548 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3549 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3550 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3551 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3552 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3553 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3554 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3555 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3556 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003557 HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
3558 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3559 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3560 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3561 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3562 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3563 {
3564 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3565 /* The multiple "Capture Source" controls confuse alsamixer
3566 * So call somewhat different..
3567 */
3568 /* .name = "Capture Source", */
3569 .name = "Input Source",
3570 .count = 2,
3571 .info = ad198x_mux_enum_info,
3572 .get = ad198x_mux_enum_get,
3573 .put = ad198x_mux_enum_put,
3574 },
3575 { } /* end */
3576};
3577
Takashi Iwai2bac6472007-05-18 18:21:41 +02003578/* Digial MIC ADC NID 0x05 + 0x06 */
3579static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3580 struct hda_codec *codec,
3581 unsigned int stream_tag,
3582 unsigned int format,
3583 struct snd_pcm_substream *substream)
3584{
3585 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3586 stream_tag, 0, format);
3587 return 0;
3588}
3589
3590static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3591 struct hda_codec *codec,
3592 struct snd_pcm_substream *substream)
3593{
Takashi Iwai888afa12008-03-18 09:57:50 +01003594 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003595 return 0;
3596}
3597
3598static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3599 .substreams = 2,
3600 .channels_min = 2,
3601 .channels_max = 2,
3602 .nid = 0x05,
3603 .ops = {
3604 .prepare = ad1984_pcm_dmic_prepare,
3605 .cleanup = ad1984_pcm_dmic_cleanup
3606 },
3607};
3608
3609static int ad1984_build_pcms(struct hda_codec *codec)
3610{
3611 struct ad198x_spec *spec = codec->spec;
3612 struct hda_pcm *info;
3613 int err;
3614
3615 err = ad198x_build_pcms(codec);
3616 if (err < 0)
3617 return err;
3618
3619 info = spec->pcm_rec + codec->num_pcms;
3620 codec->num_pcms++;
3621 info->name = "AD1984 Digital Mic";
3622 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3623 return 0;
3624}
3625
3626/* models */
3627enum {
3628 AD1984_BASIC,
3629 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003630 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003631 AD1984_MODELS
3632};
3633
3634static const char *ad1984_models[AD1984_MODELS] = {
3635 [AD1984_BASIC] = "basic",
3636 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003637 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003638};
3639
3640static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3641 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003642 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003643 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Luke Yelavich0f9f1ee92010-09-21 17:05:46 +10003644 SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", 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 Young68c18692010-08-03 01:46:44 -05003666 if (codec->subsystem_id == 0x17aa20fb) {
3667 /* Thinpad X300 does not have the ability to do SPDIF,
3668 or attach to docking station to use SPDIF */
3669 spec->multiout.dig_out_nid = 0;
3670 } else
3671 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003672 spec->input_mux = &ad1984_thinkpad_capture_source;
3673 spec->mixers[0] = ad1984_thinkpad_mixers;
3674 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003675 spec->analog_beep = 1;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003676 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003677 case AD1984_DELL_DESKTOP:
3678 spec->multiout.dig_out_nid = 0;
3679 spec->input_mux = &ad1984_dell_desktop_capture_source;
3680 spec->mixers[0] = ad1984_dell_desktop_mixers;
3681 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003682 }
3683 return 0;
3684}
3685
3686
3687/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003688 * AD1883 / AD1884A / AD1984A / AD1984B
3689 *
3690 * port-B (0x14) - front mic-in
3691 * port-E (0x1c) - rear mic-in
3692 * port-F (0x16) - CD / ext out
3693 * port-C (0x15) - rear line-in
3694 * port-D (0x12) - rear line-out
3695 * port-A (0x11) - front hp-out
3696 *
3697 * AD1984A = AD1884A + digital-mic
3698 * AD1883 = equivalent with AD1984A
3699 * AD1984B = AD1984A + extra SPDIF-out
3700 *
3701 * FIXME:
3702 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3703 */
3704
3705static hda_nid_t ad1884a_dac_nids[1] = {
3706 0x03,
3707};
3708
3709#define ad1884a_adc_nids ad1884_adc_nids
3710#define ad1884a_capsrc_nids ad1884_capsrc_nids
3711
3712#define AD1884A_SPDIF_OUT 0x02
3713
3714static struct hda_input_mux ad1884a_capture_source = {
3715 .num_items = 5,
3716 .items = {
3717 { "Front Mic", 0x0 },
3718 { "Mic", 0x4 },
3719 { "Line", 0x1 },
3720 { "CD", 0x2 },
3721 { "Mix", 0x3 },
3722 },
3723};
3724
3725static struct snd_kcontrol_new ad1884a_base_mixers[] = {
3726 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3727 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3728 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3729 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3730 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3731 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3732 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3733 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3734 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3735 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3736 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3737 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3738 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3739 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3740 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3741 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003742 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3743 HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
3744 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3745 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3746 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3747 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3748 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3749 {
3750 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3751 /* The multiple "Capture Source" controls confuse alsamixer
3752 * So call somewhat different..
3753 */
3754 /* .name = "Capture Source", */
3755 .name = "Input Source",
3756 .count = 2,
3757 .info = ad198x_mux_enum_info,
3758 .get = ad198x_mux_enum_get,
3759 .put = ad198x_mux_enum_put,
3760 },
3761 /* SPDIF controls */
3762 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3763 {
3764 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3765 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3766 /* identical with ad1983 */
3767 .info = ad1983_spdif_route_info,
3768 .get = ad1983_spdif_route_get,
3769 .put = ad1983_spdif_route_put,
3770 },
3771 { } /* end */
3772};
3773
3774/*
3775 * initialization verbs
3776 */
3777static struct hda_verb ad1884a_init_verbs[] = {
3778 /* DACs; unmute as default */
3779 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3780 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3781 /* Port-A (HP) mixer - route only from analog mixer */
3782 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3783 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3784 /* Port-A pin */
3785 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3786 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3787 /* Port-D (Line-out) mixer - route only from analog mixer */
3788 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3789 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3790 /* Port-D pin */
3791 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3792 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3793 /* Mono-out mixer - route only from analog mixer */
3794 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3795 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3796 /* Mono-out pin */
3797 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3798 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3799 /* Port-B (front mic) pin */
3800 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003801 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003802 /* Port-C (rear line-in) pin */
3803 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003804 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003805 /* Port-E (rear mic) pin */
3806 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3807 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3808 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
3809 /* Port-F (CD) pin */
3810 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3811 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3812 /* Analog mixer; mute as default */
3813 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3814 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3815 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3816 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3817 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
3818 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3819 /* Analog Mix output amp */
3820 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3821 /* capture sources */
3822 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
3823 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3824 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3825 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3826 /* SPDIF output amp */
3827 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3828 { } /* end */
3829};
3830
3831#ifdef CONFIG_SND_HDA_POWER_SAVE
3832static struct hda_amp_list ad1884a_loopbacks[] = {
3833 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3834 { 0x20, HDA_INPUT, 1 }, /* Mic */
3835 { 0x20, HDA_INPUT, 2 }, /* CD */
3836 { 0x20, HDA_INPUT, 4 }, /* Docking */
3837 { } /* end */
3838};
3839#endif
3840
3841/*
3842 * Laptop model
3843 *
3844 * Port A: Headphone jack
3845 * Port B: MIC jack
3846 * Port C: Internal MIC
3847 * Port D: Dock Line Out (if enabled)
3848 * Port E: Dock Line In (if enabled)
3849 * Port F: Internal speakers
3850 */
3851
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003852static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
3853 struct snd_ctl_elem_value *ucontrol)
3854{
3855 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3856 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
3857 int mute = (!ucontrol->value.integer.value[0] &&
3858 !ucontrol->value.integer.value[1]);
3859 /* toggle GPIO1 according to the mute state */
3860 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
3861 mute ? 0x02 : 0x0);
3862 return ret;
3863}
Takashi Iwaic5059252008-02-16 09:43:56 +01003864
3865static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
3866 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003867 {
3868 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3869 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003870 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003871 .info = snd_hda_mixer_amp_switch_info,
3872 .get = snd_hda_mixer_amp_switch_get,
3873 .put = ad1884a_mobile_master_sw_put,
3874 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3875 },
Takashi Iwaic5059252008-02-16 09:43:56 +01003876 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3877 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3878 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3879 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3880 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3881 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3882 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3883 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3884 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003885 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3886 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
3887 HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3888 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3889 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003890 { } /* end */
3891};
3892
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003893static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
3894 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db172009-07-02 16:10:23 +02003895 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
3896 {
3897 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3898 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003899 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai099db172009-07-02 16:10:23 +02003900 .info = snd_hda_mixer_amp_switch_info,
3901 .get = snd_hda_mixer_amp_switch_get,
3902 .put = ad1884a_mobile_master_sw_put,
3903 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3904 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003905 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3906 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02003907 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
3908 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003909 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3910 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003911 { } /* end */
3912};
3913
Takashi Iwaic5059252008-02-16 09:43:56 +01003914/* mute internal speaker if HP is plugged */
3915static void ad1884a_hp_automute(struct hda_codec *codec)
3916{
3917 unsigned int present;
3918
Takashi Iwaid56757a2009-11-18 08:00:14 +01003919 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01003920 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3921 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3922 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3923 present ? 0x00 : 0x02);
3924}
3925
Takashi Iwai269ef192008-05-30 15:32:15 +02003926/* switch to external mic if plugged */
3927static void ad1884a_hp_automic(struct hda_codec *codec)
3928{
3929 unsigned int present;
3930
Takashi Iwaid56757a2009-11-18 08:00:14 +01003931 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02003932 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
3933 present ? 0 : 1);
3934}
3935
Takashi Iwaic5059252008-02-16 09:43:56 +01003936#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02003937#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01003938
3939/* unsolicited event for HP jack sensing */
3940static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
3941{
Takashi Iwai269ef192008-05-30 15:32:15 +02003942 switch (res >> 26) {
3943 case AD1884A_HP_EVENT:
3944 ad1884a_hp_automute(codec);
3945 break;
3946 case AD1884A_MIC_EVENT:
3947 ad1884a_hp_automic(codec);
3948 break;
3949 }
Takashi Iwaic5059252008-02-16 09:43:56 +01003950}
3951
3952/* initialize jack-sensing, too */
3953static int ad1884a_hp_init(struct hda_codec *codec)
3954{
3955 ad198x_init(codec);
3956 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02003957 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01003958 return 0;
3959}
3960
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003961/* mute internal speaker if HP or docking HP is plugged */
3962static void ad1884a_laptop_automute(struct hda_codec *codec)
3963{
3964 unsigned int present;
3965
Takashi Iwaid56757a2009-11-18 08:00:14 +01003966 present = snd_hda_jack_detect(codec, 0x11);
3967 if (!present)
3968 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003969 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3970 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3971 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3972 present ? 0x00 : 0x02);
3973}
3974
3975/* switch to external mic if plugged */
3976static void ad1884a_laptop_automic(struct hda_codec *codec)
3977{
3978 unsigned int idx;
3979
Takashi Iwaid56757a2009-11-18 08:00:14 +01003980 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003981 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01003982 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003983 idx = 4;
3984 else
3985 idx = 1;
3986 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
3987}
3988
3989/* unsolicited event for HP jack sensing */
3990static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
3991 unsigned int res)
3992{
3993 switch (res >> 26) {
3994 case AD1884A_HP_EVENT:
3995 ad1884a_laptop_automute(codec);
3996 break;
3997 case AD1884A_MIC_EVENT:
3998 ad1884a_laptop_automic(codec);
3999 break;
4000 }
4001}
4002
4003/* initialize jack-sensing, too */
4004static int ad1884a_laptop_init(struct hda_codec *codec)
4005{
4006 ad198x_init(codec);
4007 ad1884a_laptop_automute(codec);
4008 ad1884a_laptop_automic(codec);
4009 return 0;
4010}
4011
Takashi Iwaic5059252008-02-16 09:43:56 +01004012/* additional verbs for laptop model */
4013static struct hda_verb ad1884a_laptop_verbs[] = {
4014 /* Port-A (HP) pin - always unmuted */
4015 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4016 /* Port-F (int speaker) mixer - route only from analog mixer */
4017 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4018 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08004019 /* Port-F (int speaker) pin */
4020 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01004021 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08004022 /* required for compaq 6530s/6531s speaker output */
4023 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004024 /* Port-C pin - internal mic-in */
4025 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4026 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4027 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02004028 /* Port-D (docking line-out) pin - default unmuted */
4029 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01004030 /* analog mix */
4031 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4032 /* unsolicited event for pin-sense */
4033 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004034 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004035 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004036 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02004037 /* allow to touch GPIO1 (for mute control) */
4038 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4039 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4040 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01004041 { } /* end */
4042};
4043
Takashi Iwai73156132009-04-23 08:24:48 +02004044static struct hda_verb ad1884a_mobile_verbs[] = {
4045 /* DACs; unmute as default */
4046 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4047 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4048 /* Port-A (HP) mixer - route only from analog mixer */
4049 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4050 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4051 /* Port-A pin */
4052 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4053 /* Port-A (HP) pin - always unmuted */
4054 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4055 /* Port-B (mic jack) pin */
4056 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4057 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4058 /* Port-C (int mic) pin */
4059 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4060 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4061 /* Port-F (int speaker) mixer - route only from analog mixer */
4062 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4063 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4064 /* Port-F pin */
4065 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4066 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4067 /* Analog mixer; mute as default */
4068 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4069 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4070 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4071 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4072 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4073 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4074 /* Analog Mix output amp */
4075 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4076 /* capture sources */
4077 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4078 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4079 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4080 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4081 /* unsolicited event for pin-sense */
4082 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4083 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db172009-07-02 16:10:23 +02004084 /* allow to touch GPIO1 (for mute control) */
4085 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4086 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4087 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02004088 { } /* end */
4089};
4090
Takashi Iwaic5059252008-02-16 09:43:56 +01004091/*
Takashi Iwaif0813742008-03-18 12:13:03 +01004092 * Thinkpad X300
4093 * 0x11 - HP
4094 * 0x12 - speaker
4095 * 0x14 - mic-in
4096 * 0x17 - built-in mic
4097 */
4098
4099static struct hda_verb ad1984a_thinkpad_verbs[] = {
4100 /* HP unmute */
4101 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4102 /* analog mix */
4103 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4104 /* turn on EAPD */
4105 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4106 /* unsolicited event for pin-sense */
4107 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4108 /* internal mic - dmic */
4109 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02004110 /* set magic COEFs for dmic */
4111 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4112 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01004113 { } /* end */
4114};
4115
4116static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
4117 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4118 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4119 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4120 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4121 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4122 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01004123 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
4124 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4125 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4126 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4127 {
4128 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4129 .name = "Capture Source",
4130 .info = ad198x_mux_enum_info,
4131 .get = ad198x_mux_enum_get,
4132 .put = ad198x_mux_enum_put,
4133 },
4134 { } /* end */
4135};
4136
4137static struct hda_input_mux ad1984a_thinkpad_capture_source = {
4138 .num_items = 3,
4139 .items = {
4140 { "Mic", 0x0 },
4141 { "Internal Mic", 0x5 },
4142 { "Mix", 0x3 },
4143 },
4144};
4145
4146/* mute internal speaker if HP is plugged */
4147static void ad1984a_thinkpad_automute(struct hda_codec *codec)
4148{
4149 unsigned int present;
4150
Takashi Iwaid56757a2009-11-18 08:00:14 +01004151 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01004152 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
4153 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4154}
4155
4156/* unsolicited event for HP jack sensing */
4157static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
4158 unsigned int res)
4159{
4160 if ((res >> 26) != AD1884A_HP_EVENT)
4161 return;
4162 ad1984a_thinkpad_automute(codec);
4163}
4164
4165/* initialize jack-sensing, too */
4166static int ad1984a_thinkpad_init(struct hda_codec *codec)
4167{
4168 ad198x_init(codec);
4169 ad1984a_thinkpad_automute(codec);
4170 return 0;
4171}
4172
4173/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004174 * HP Touchsmart
4175 * port-A (0x11) - front hp-out
4176 * port-B (0x14) - unused
4177 * port-C (0x15) - unused
4178 * port-D (0x12) - rear line out
4179 * port-E (0x1c) - front mic-in
4180 * port-F (0x16) - Internal speakers
4181 * digital-mic (0x17) - Internal mic
4182 */
4183
4184static struct hda_verb ad1984a_touchsmart_verbs[] = {
4185 /* DACs; unmute as default */
4186 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4187 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4188 /* Port-A (HP) mixer - route only from analog mixer */
4189 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4190 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4191 /* Port-A pin */
4192 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4193 /* Port-A (HP) pin - always unmuted */
4194 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4195 /* Port-E (int speaker) mixer - route only from analog mixer */
4196 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4197 /* Port-E pin */
4198 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4199 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4200 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4201 /* Port-F (int speaker) mixer - route only from analog mixer */
4202 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4203 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4204 /* Port-F pin */
4205 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4206 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4207 /* Analog mixer; mute as default */
4208 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4209 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4210 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4211 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4212 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4213 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4214 /* Analog Mix output amp */
4215 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4216 /* capture sources */
4217 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4218 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4219 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4220 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4221 /* unsolicited event for pin-sense */
4222 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4223 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4224 /* allow to touch GPIO1 (for mute control) */
4225 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4226 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4227 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4228 /* internal mic - dmic */
4229 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4230 /* set magic COEFs for dmic */
4231 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4232 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4233 { } /* end */
4234};
4235
4236static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
4237 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4238/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4239 {
4240 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004241 .subdevice = HDA_SUBDEV_AMP_FLAG,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004242 .name = "Master Playback Switch",
4243 .info = snd_hda_mixer_amp_switch_info,
4244 .get = snd_hda_mixer_amp_switch_get,
4245 .put = ad1884a_mobile_master_sw_put,
4246 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4247 },
4248 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4249 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4250 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4251 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4252 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
4253 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4254 { } /* end */
4255};
4256
4257/* switch to external mic if plugged */
4258static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4259{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004260 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004261 snd_hda_codec_write(codec, 0x0c, 0,
4262 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004263 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004264 snd_hda_codec_write(codec, 0x0c, 0,
4265 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004266}
4267
4268
4269/* unsolicited event for HP jack sensing */
4270static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4271 unsigned int res)
4272{
4273 switch (res >> 26) {
4274 case AD1884A_HP_EVENT:
4275 ad1884a_hp_automute(codec);
4276 break;
4277 case AD1884A_MIC_EVENT:
4278 ad1984a_touchsmart_automic(codec);
4279 break;
4280 }
4281}
4282
4283/* initialize jack-sensing, too */
4284static int ad1984a_touchsmart_init(struct hda_codec *codec)
4285{
4286 ad198x_init(codec);
4287 ad1884a_hp_automute(codec);
4288 ad1984a_touchsmart_automic(codec);
4289 return 0;
4290}
4291
4292
4293/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004294 */
4295
4296enum {
4297 AD1884A_DESKTOP,
4298 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004299 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004300 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004301 AD1984A_TOUCHSMART,
Takashi Iwaic5059252008-02-16 09:43:56 +01004302 AD1884A_MODELS
4303};
4304
4305static const char *ad1884a_models[AD1884A_MODELS] = {
4306 [AD1884A_DESKTOP] = "desktop",
4307 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004308 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004309 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004310 [AD1984A_TOUCHSMART] = "touchsmart",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004311};
4312
4313static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
4314 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004315 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004316 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004317 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004318 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004319 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4320 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004321 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004322 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004323 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004324 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004325};
4326
4327static int patch_ad1884a(struct hda_codec *codec)
4328{
4329 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004330 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004331
4332 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4333 if (spec == NULL)
4334 return -ENOMEM;
4335
Takashi Iwaic5059252008-02-16 09:43:56 +01004336 codec->spec = spec;
4337
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004338 err = snd_hda_attach_beep_device(codec, 0x10);
4339 if (err < 0) {
4340 ad198x_free(codec);
4341 return err;
4342 }
4343 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4344
Takashi Iwaic5059252008-02-16 09:43:56 +01004345 spec->multiout.max_channels = 2;
4346 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4347 spec->multiout.dac_nids = ad1884a_dac_nids;
4348 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4349 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4350 spec->adc_nids = ad1884a_adc_nids;
4351 spec->capsrc_nids = ad1884a_capsrc_nids;
4352 spec->input_mux = &ad1884a_capture_source;
4353 spec->num_mixers = 1;
4354 spec->mixers[0] = ad1884a_base_mixers;
4355 spec->num_init_verbs = 1;
4356 spec->init_verbs[0] = ad1884a_init_verbs;
4357 spec->spdif_route = 0;
4358#ifdef CONFIG_SND_HDA_POWER_SAVE
4359 spec->loopback.amplist = ad1884a_loopbacks;
4360#endif
4361 codec->patch_ops = ad198x_patch_ops;
4362
4363 /* override some parameters */
4364 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004365 ad1884a_models,
4366 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01004367 switch (board_config) {
4368 case AD1884A_LAPTOP:
4369 spec->mixers[0] = ad1884a_laptop_mixers;
4370 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4371 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004372 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4373 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004374 /* set the upper-limit for mixer amp to 0dB for avoiding the
4375 * possible damage by overloading
4376 */
4377 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4378 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4379 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4380 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4381 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004382 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004383 case AD1884A_MOBILE:
4384 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004385 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004386 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004387 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4388 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004389 /* set the upper-limit for mixer amp to 0dB for avoiding the
4390 * possible damage by overloading
4391 */
4392 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4393 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4394 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4395 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4396 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004397 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004398 case AD1884A_THINKPAD:
4399 spec->mixers[0] = ad1984a_thinkpad_mixers;
4400 spec->init_verbs[spec->num_init_verbs++] =
4401 ad1984a_thinkpad_verbs;
4402 spec->multiout.dig_out_nid = 0;
4403 spec->input_mux = &ad1984a_thinkpad_capture_source;
4404 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4405 codec->patch_ops.init = ad1984a_thinkpad_init;
4406 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004407 case AD1984A_TOUCHSMART:
4408 spec->mixers[0] = ad1984a_touchsmart_mixers;
4409 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4410 spec->multiout.dig_out_nid = 0;
4411 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4412 codec->patch_ops.init = ad1984a_touchsmart_init;
4413 /* set the upper-limit for mixer amp to 0dB for avoiding the
4414 * possible damage by overloading
4415 */
4416 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4417 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4418 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4419 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4420 (1 << AC_AMPCAP_MUTE_SHIFT));
4421 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004422 }
4423
Takashi Iwai729d55b2009-12-25 22:49:01 +01004424 codec->no_trigger_sense = 1;
4425
Takashi Iwaic5059252008-02-16 09:43:56 +01004426 return 0;
4427}
4428
4429
4430/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004431 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004432 *
4433 * port-A - front hp-out
4434 * port-B - front mic-in
4435 * port-C - rear line-in, shared surr-out (3stack)
4436 * port-D - rear line-out
4437 * port-E - rear mic-in, shared clfe-out (3stack)
4438 * port-F - rear surr-out (6stack)
4439 * port-G - rear clfe-out (6stack)
4440 */
4441
4442static hda_nid_t ad1882_dac_nids[3] = {
4443 0x04, 0x03, 0x05
4444};
4445
4446static hda_nid_t ad1882_adc_nids[2] = {
4447 0x08, 0x09,
4448};
4449
4450static hda_nid_t ad1882_capsrc_nids[2] = {
4451 0x0c, 0x0d,
4452};
4453
4454#define AD1882_SPDIF_OUT 0x02
4455
4456/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
4457static struct hda_input_mux ad1882_capture_source = {
4458 .num_items = 5,
4459 .items = {
4460 { "Front Mic", 0x1 },
4461 { "Mic", 0x4 },
4462 { "Line", 0x2 },
4463 { "CD", 0x3 },
4464 { "Mix", 0x7 },
4465 },
4466};
4467
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004468/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
4469static struct hda_input_mux ad1882a_capture_source = {
4470 .num_items = 5,
4471 .items = {
4472 { "Front Mic", 0x1 },
4473 { "Mic", 0x4},
4474 { "Line", 0x2 },
4475 { "Digital Mic", 0x06 },
4476 { "Mix", 0x7 },
4477 },
4478};
4479
Takashi Iwai0ac85512007-06-20 15:46:13 +02004480static struct snd_kcontrol_new ad1882_base_mixers[] = {
4481 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4482 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4483 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4484 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4485 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4486 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4487 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4488 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004489
Takashi Iwai0ac85512007-06-20 15:46:13 +02004490 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
4491 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
4492 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
4493 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4494 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4495 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4496 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4497 {
4498 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4499 /* The multiple "Capture Source" controls confuse alsamixer
4500 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004501 */
4502 /* .name = "Capture Source", */
4503 .name = "Input Source",
4504 .count = 2,
4505 .info = ad198x_mux_enum_info,
4506 .get = ad198x_mux_enum_get,
4507 .put = ad198x_mux_enum_put,
4508 },
4509 /* SPDIF controls */
4510 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4511 {
4512 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4513 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4514 /* identical with ad1983 */
4515 .info = ad1983_spdif_route_info,
4516 .get = ad1983_spdif_route_get,
4517 .put = ad1983_spdif_route_put,
4518 },
4519 { } /* end */
4520};
4521
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004522static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
4523 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4524 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4525 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4526 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4527 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4528 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4529 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4530 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004531 { } /* end */
4532};
4533
4534static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
4535 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4536 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4537 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4538 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4539 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4540 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4541 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4542 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004543 HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
4544 { } /* end */
4545};
4546
Takashi Iwai0ac85512007-06-20 15:46:13 +02004547static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
4548 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4549 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4550 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4551 {
4552 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4553 .name = "Channel Mode",
4554 .info = ad198x_ch_mode_info,
4555 .get = ad198x_ch_mode_get,
4556 .put = ad198x_ch_mode_put,
4557 },
4558 { } /* end */
4559};
4560
4561static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
4562 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4563 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4564 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4565 { } /* end */
4566};
4567
4568static struct hda_verb ad1882_ch2_init[] = {
4569 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4570 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4571 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4572 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4573 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4574 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4575 { } /* end */
4576};
4577
4578static struct hda_verb ad1882_ch4_init[] = {
4579 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4580 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4581 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4582 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4583 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4584 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4585 { } /* end */
4586};
4587
4588static struct hda_verb ad1882_ch6_init[] = {
4589 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4590 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4591 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4592 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4593 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4594 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4595 { } /* end */
4596};
4597
4598static struct hda_channel_mode ad1882_modes[3] = {
4599 { 2, ad1882_ch2_init },
4600 { 4, ad1882_ch4_init },
4601 { 6, ad1882_ch6_init },
4602};
4603
4604/*
4605 * initialization verbs
4606 */
4607static struct hda_verb ad1882_init_verbs[] = {
4608 /* DACs; mute as default */
4609 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4610 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4611 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4612 /* Port-A (HP) mixer */
4613 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4614 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4615 /* Port-A pin */
4616 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4617 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4618 /* HP selector - select DAC2 */
4619 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4620 /* Port-D (Line-out) mixer */
4621 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4622 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4623 /* Port-D pin */
4624 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4625 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4626 /* Mono-out mixer */
4627 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4628 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4629 /* Mono-out pin */
4630 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4631 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4632 /* Port-B (front mic) pin */
4633 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4634 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4635 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4636 /* Port-C (line-in) pin */
4637 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4638 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4639 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4640 /* Port-C mixer - mute as input */
4641 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4642 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4643 /* Port-E (mic-in) pin */
4644 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4645 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4646 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4647 /* Port-E mixer - mute as input */
4648 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4649 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4650 /* Port-F (surround) */
4651 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4652 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4653 /* Port-G (CLFE) */
4654 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4655 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4656 /* Analog mixer; mute as default */
4657 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4658 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4659 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4660 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4661 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4662 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4663 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4664 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4665 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4666 /* Analog Mix output amp */
4667 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4668 /* SPDIF output selector */
4669 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4670 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4671 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4672 { } /* end */
4673};
4674
Takashi Iwaicb53c622007-08-10 17:21:45 +02004675#ifdef CONFIG_SND_HDA_POWER_SAVE
4676static struct hda_amp_list ad1882_loopbacks[] = {
4677 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4678 { 0x20, HDA_INPUT, 1 }, /* Mic */
4679 { 0x20, HDA_INPUT, 4 }, /* Line */
4680 { 0x20, HDA_INPUT, 6 }, /* CD */
4681 { } /* end */
4682};
4683#endif
4684
Takashi Iwai0ac85512007-06-20 15:46:13 +02004685/* models */
4686enum {
4687 AD1882_3STACK,
4688 AD1882_6STACK,
4689 AD1882_MODELS
4690};
4691
4692static const char *ad1882_models[AD1986A_MODELS] = {
4693 [AD1882_3STACK] = "3stack",
4694 [AD1882_6STACK] = "6stack",
4695};
4696
4697
4698static int patch_ad1882(struct hda_codec *codec)
4699{
4700 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004701 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004702
4703 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4704 if (spec == NULL)
4705 return -ENOMEM;
4706
Takashi Iwai0ac85512007-06-20 15:46:13 +02004707 codec->spec = spec;
4708
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004709 err = snd_hda_attach_beep_device(codec, 0x10);
4710 if (err < 0) {
4711 ad198x_free(codec);
4712 return err;
4713 }
4714 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4715
Takashi Iwai0ac85512007-06-20 15:46:13 +02004716 spec->multiout.max_channels = 6;
4717 spec->multiout.num_dacs = 3;
4718 spec->multiout.dac_nids = ad1882_dac_nids;
4719 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
4720 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
4721 spec->adc_nids = ad1882_adc_nids;
4722 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004723 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004724 spec->input_mux = &ad1882_capture_source;
4725 else
4726 spec->input_mux = &ad1882a_capture_source;
4727 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004728 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004729 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004730 spec->mixers[1] = ad1882_loopback_mixers;
4731 else
4732 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004733 spec->num_init_verbs = 1;
4734 spec->init_verbs[0] = ad1882_init_verbs;
4735 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02004736#ifdef CONFIG_SND_HDA_POWER_SAVE
4737 spec->loopback.amplist = ad1882_loopbacks;
4738#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01004739 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004740
4741 codec->patch_ops = ad198x_patch_ops;
4742
4743 /* override some parameters */
4744 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
4745 ad1882_models, NULL);
4746 switch (board_config) {
4747 default:
4748 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004749 spec->num_mixers = 3;
4750 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004751 spec->channel_mode = ad1882_modes;
4752 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
4753 spec->need_dac_fix = 1;
4754 spec->multiout.max_channels = 2;
4755 spec->multiout.num_dacs = 1;
4756 break;
4757 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004758 spec->num_mixers = 3;
4759 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004760 break;
4761 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01004762
4763 codec->no_trigger_sense = 1;
4764
Takashi Iwai0ac85512007-06-20 15:46:13 +02004765 return 0;
4766}
4767
4768
4769/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004770 * patch entries
4771 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004772static struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004773 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02004774 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004775 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004776 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004777 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
4778 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02004779 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
4780 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004781 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004782 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01004783 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02004784 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004785 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02004786 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
4787 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004788 {} /* terminator */
4789};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004790
4791MODULE_ALIAS("snd-hda-codec-id:11d4*");
4792
4793MODULE_LICENSE("GPL");
4794MODULE_DESCRIPTION("Analog Devices HD-audio codec");
4795
4796static struct hda_codec_preset_list analog_list = {
4797 .preset = snd_hda_preset_analog,
4798 .owner = THIS_MODULE,
4799};
4800
4801static int __init patch_analog_init(void)
4802{
4803 return snd_hda_add_codec_preset(&analog_list);
4804}
4805
4806static void __exit patch_analog_exit(void)
4807{
4808 snd_hda_delete_codec_preset(&analog_list);
4809}
4810
4811module_init(patch_analog_init)
4812module_exit(patch_analog_exit)