blob: e75b5e5a1d556f82d2539baafee62ab95626cf61 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Takashi Iwai0ac85512007-06-20 15:46:13 +02002 * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
3 * AD1986A, AD1988
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
Takashi Iwai2bac6472007-05-18 18:21:41 +02005 * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 *
7 * This driver is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This driver is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/init.h>
23#include <linux/delay.h>
24#include <linux/slab.h>
25#include <linux/pci.h>
Ingo Molnar62932df2006-01-16 16:34:20 +010026
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <sound/core.h>
28#include "hda_codec.h"
29#include "hda_local.h"
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010030#include "hda_beep.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020032struct ad198x_spec {
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010033 struct snd_kcontrol_new *mixers[5];
Takashi Iwai985be542005-11-02 18:26:49 +010034 int num_mixers;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010035 unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
Takashi Iwaid32410b12005-11-24 16:06:23 +010036 const struct hda_verb *init_verbs[5]; /* initialization verbs
Takashi Iwai985be542005-11-02 18:26:49 +010037 * don't forget NULL termination!
38 */
39 unsigned int num_init_verbs;
40
41 /* playback */
42 struct hda_multi_out multiout; /* playback set-up
43 * max_channels, dacs must be set
44 * dig_out_nid and hp_nid are optional
45 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +010046 unsigned int cur_eapd;
Takashi Iwai2125cad2006-03-27 12:52:22 +020047 unsigned int need_dac_fix;
Takashi Iwai985be542005-11-02 18:26:49 +010048
49 /* capture */
50 unsigned int num_adc_nids;
51 hda_nid_t *adc_nids;
52 hda_nid_t dig_in_nid; /* digital-in NID; optional */
53
54 /* capture source */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020055 const struct hda_input_mux *input_mux;
Takashi Iwai2e5b9562005-11-21 16:36:15 +010056 hda_nid_t *capsrc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010057 unsigned int cur_mux[3];
58
59 /* channel model */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +010060 const struct hda_channel_mode *channel_mode;
Takashi Iwai985be542005-11-02 18:26:49 +010061 int num_channel_mode;
62
63 /* PCM information */
Takashi Iwai2bac6472007-05-18 18:21:41 +020064 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
Takashi Iwai985be542005-11-02 18:26:49 +010065
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020066 unsigned int spdif_route;
Takashi Iwaid32410b12005-11-24 16:06:23 +010067
68 /* dynamic controls, init_verbs and input_mux */
69 struct auto_pin_cfg autocfg;
Takashi Iwai603c4012008-07-30 15:01:44 +020070 struct snd_array kctls;
Takashi Iwaid32410b12005-11-24 16:06:23 +010071 struct hda_input_mux private_imux;
Takashi Iwai41923e42007-10-22 17:20:10 +020072 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
Takashi Iwaicb53c622007-08-10 17:21:45 +020073
Takashi Iwai8ab78c72007-09-06 14:29:53 +020074 unsigned int jack_present :1;
Takashi Iwaiee6e3652009-12-08 17:23:33 +010075 unsigned int inv_jack_detect:1; /* inverted jack-detection */
76 unsigned int inv_eapd:1; /* inverted EAPD implementation */
Takashi Iwai8ab78c72007-09-06 14:29:53 +020077
Takashi Iwaicb53c622007-08-10 17:21:45 +020078#ifdef CONFIG_SND_HDA_POWER_SAVE
79 struct hda_loopback_check loopback;
80#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +010081 /* for virtual master */
82 hda_nid_t vmaster_nid;
Takashi Iwai2134ea42008-01-10 16:53:55 +010083 const char **slave_vols;
84 const char **slave_sws;
Linus Torvalds1da177e2005-04-16 15:20:36 -070085};
86
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020087/*
88 * input MUX handling (common part)
89 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010090static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020091{
92 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
93 struct ad198x_spec *spec = codec->spec;
94
95 return snd_hda_input_mux_info(spec->input_mux, uinfo);
96}
97
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010098static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020099{
100 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
101 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100102 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200103
Takashi Iwai985be542005-11-02 18:26:49 +0100104 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200105 return 0;
106}
107
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100108static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200109{
110 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
111 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100112 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200113
114 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai2e5b9562005-11-21 16:36:15 +0100115 spec->capsrc_nids[adc_idx],
116 &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200117}
118
119/*
120 * initialization (common callbacks)
121 */
122static int ad198x_init(struct hda_codec *codec)
123{
124 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100125 int i;
126
127 for (i = 0; i < spec->num_init_verbs; i++)
128 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200129 return 0;
130}
131
Takashi Iwai2134ea42008-01-10 16:53:55 +0100132static const char *ad_slave_vols[] = {
133 "Front Playback Volume",
134 "Surround Playback Volume",
135 "Center Playback Volume",
136 "LFE Playback Volume",
137 "Side Playback Volume",
138 "Headphone Playback Volume",
139 "Mono Playback Volume",
Takashi Iwai628ed132008-01-25 11:56:57 +0100140 "Speaker Playback Volume",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100141 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100142 NULL
143};
144
145static const char *ad_slave_sws[] = {
146 "Front Playback Switch",
147 "Surround Playback Switch",
148 "Center Playback Switch",
149 "LFE Playback Switch",
150 "Side Playback Switch",
151 "Headphone Playback Switch",
152 "Mono Playback Switch",
Takashi Iwai628ed132008-01-25 11:56:57 +0100153 "Speaker Playback Switch",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100154 "IEC958 Playback Switch",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100155 NULL
156};
157
Takashi Iwai603c4012008-07-30 15:01:44 +0200158static void ad198x_free_kctls(struct hda_codec *codec);
159
Takashi Iwai67d634c2009-11-16 15:35:59 +0100160#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100161/* additional beep mixers; the actual parameters are overwritten at build */
162static struct snd_kcontrol_new ad_beep_mixer[] = {
163 HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
Jaroslav Kysela123c07a2009-10-21 14:48:23 +0200164 HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100165 { } /* end */
166};
167
168#define set_beep_amp(spec, nid, idx, dir) \
169 ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100170#else
171#define set_beep_amp(spec, nid, idx, dir) /* NOP */
172#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100173
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200174static int ad198x_build_controls(struct hda_codec *codec)
175{
176 struct ad198x_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100177 struct snd_kcontrol *kctl;
Takashi Iwai985be542005-11-02 18:26:49 +0100178 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200179 int err;
180
Takashi Iwai985be542005-11-02 18:26:49 +0100181 for (i = 0; i < spec->num_mixers; i++) {
182 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
183 if (err < 0)
184 return err;
185 }
186 if (spec->multiout.dig_out_nid) {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200187 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100188 if (err < 0)
189 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100190 err = snd_hda_create_spdif_share_sw(codec,
191 &spec->multiout);
192 if (err < 0)
193 return err;
194 spec->multiout.share_spdif = 1;
Takashi Iwai985be542005-11-02 18:26:49 +0100195 }
196 if (spec->dig_in_nid) {
197 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
198 if (err < 0)
199 return err;
200 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100201
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100202 /* create beep controls if needed */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100203#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100204 if (spec->beep_amp) {
205 struct snd_kcontrol_new *knew;
206 for (knew = ad_beep_mixer; knew->name; knew++) {
207 struct snd_kcontrol *kctl;
208 kctl = snd_ctl_new1(knew, codec);
209 if (!kctl)
210 return -ENOMEM;
211 kctl->private_value = spec->beep_amp;
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100212 err = snd_hda_ctl_add(codec, 0, kctl);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100213 if (err < 0)
214 return err;
215 }
216 }
Takashi Iwai67d634c2009-11-16 15:35:59 +0100217#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100218
Takashi Iwai2134ea42008-01-10 16:53:55 +0100219 /* if we have no master control, let's create it */
220 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100221 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100222 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100223 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100224 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100225 vmaster_tlv,
Takashi Iwai2134ea42008-01-10 16:53:55 +0100226 (spec->slave_vols ?
227 spec->slave_vols : ad_slave_vols));
228 if (err < 0)
229 return err;
230 }
231 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
232 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
233 NULL,
234 (spec->slave_sws ?
235 spec->slave_sws : ad_slave_sws));
236 if (err < 0)
237 return err;
238 }
239
Takashi Iwai603c4012008-07-30 15:01:44 +0200240 ad198x_free_kctls(codec); /* no longer needed */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100241
242 /* assign Capture Source enums to NID */
243 kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
244 if (!kctl)
245 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
246 for (i = 0; kctl && i < kctl->count; i++) {
247 err = snd_hda_add_nids(codec, kctl, i, spec->capsrc_nids,
248 spec->input_mux->num_items);
249 if (err < 0)
250 return err;
251 }
252
253 /* assign IEC958 enums to NID */
254 kctl = snd_hda_find_mixer_ctl(codec,
255 SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
256 if (kctl) {
257 err = snd_hda_add_nid(codec, kctl, 0,
258 spec->multiout.dig_out_nid);
259 if (err < 0)
260 return err;
261 }
262
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200263 return 0;
264}
265
Takashi Iwaicb53c622007-08-10 17:21:45 +0200266#ifdef CONFIG_SND_HDA_POWER_SAVE
267static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
268{
269 struct ad198x_spec *spec = codec->spec;
270 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
271}
272#endif
273
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200274/*
275 * Analog playback callbacks
276 */
277static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
278 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100279 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200280{
281 struct ad198x_spec *spec = codec->spec;
Takashi Iwai9a081602008-02-12 18:37:26 +0100282 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
283 hinfo);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200284}
285
286static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
287 struct hda_codec *codec,
288 unsigned int stream_tag,
289 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100290 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200291{
292 struct ad198x_spec *spec = codec->spec;
293 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
294 format, substream);
295}
296
297static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
298 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100299 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200300{
301 struct ad198x_spec *spec = codec->spec;
302 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
303}
304
305/*
306 * Digital out
307 */
308static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
309 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100310 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200311{
312 struct ad198x_spec *spec = codec->spec;
313 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
314}
315
316static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
317 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100318 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200319{
320 struct ad198x_spec *spec = codec->spec;
321 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
322}
323
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200324static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
325 struct hda_codec *codec,
326 unsigned int stream_tag,
327 unsigned int format,
328 struct snd_pcm_substream *substream)
329{
330 struct ad198x_spec *spec = codec->spec;
331 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
332 format, substream);
333}
334
Takashi Iwai9411e212009-02-13 11:32:28 +0100335static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
336 struct hda_codec *codec,
337 struct snd_pcm_substream *substream)
338{
339 struct ad198x_spec *spec = codec->spec;
340 return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
341}
342
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200343/*
344 * Analog capture
345 */
346static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
347 struct hda_codec *codec,
348 unsigned int stream_tag,
349 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100350 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200351{
352 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100353 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
354 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200355 return 0;
356}
357
358static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
359 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100360 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200361{
362 struct ad198x_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +0100363 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200364 return 0;
365}
366
367
368/*
369 */
370static struct hda_pcm_stream ad198x_pcm_analog_playback = {
371 .substreams = 1,
372 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100373 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200374 .nid = 0, /* fill later */
375 .ops = {
376 .open = ad198x_playback_pcm_open,
377 .prepare = ad198x_playback_pcm_prepare,
378 .cleanup = ad198x_playback_pcm_cleanup
379 },
380};
381
382static struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100383 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200384 .channels_min = 2,
385 .channels_max = 2,
386 .nid = 0, /* fill later */
387 .ops = {
388 .prepare = ad198x_capture_pcm_prepare,
389 .cleanup = ad198x_capture_pcm_cleanup
390 },
391};
392
393static struct hda_pcm_stream ad198x_pcm_digital_playback = {
394 .substreams = 1,
395 .channels_min = 2,
396 .channels_max = 2,
397 .nid = 0, /* fill later */
398 .ops = {
399 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200400 .close = ad198x_dig_playback_pcm_close,
Takashi Iwai9411e212009-02-13 11:32:28 +0100401 .prepare = ad198x_dig_playback_pcm_prepare,
402 .cleanup = ad198x_dig_playback_pcm_cleanup
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200403 },
404};
405
Takashi Iwai985be542005-11-02 18:26:49 +0100406static struct hda_pcm_stream ad198x_pcm_digital_capture = {
407 .substreams = 1,
408 .channels_min = 2,
409 .channels_max = 2,
410 /* NID is set in alc_build_pcms */
411};
412
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200413static int ad198x_build_pcms(struct hda_codec *codec)
414{
415 struct ad198x_spec *spec = codec->spec;
416 struct hda_pcm *info = spec->pcm_rec;
417
418 codec->num_pcms = 1;
419 codec->pcm_info = info;
420
421 info->name = "AD198x Analog";
422 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
423 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
424 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
425 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100426 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
427 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200428
429 if (spec->multiout.dig_out_nid) {
430 info++;
431 codec->num_pcms++;
432 info->name = "AD198x Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +0100433 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200434 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
435 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100436 if (spec->dig_in_nid) {
437 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
438 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
439 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200440 }
441
442 return 0;
443}
444
Takashi Iwai603c4012008-07-30 15:01:44 +0200445static void ad198x_free_kctls(struct hda_codec *codec)
446{
447 struct ad198x_spec *spec = codec->spec;
448
449 if (spec->kctls.list) {
450 struct snd_kcontrol_new *kctl = spec->kctls.list;
451 int i;
452 for (i = 0; i < spec->kctls.used; i++)
453 kfree(kctl[i].name);
454 }
455 snd_array_free(&spec->kctls);
456}
457
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200458static void ad198x_free(struct hda_codec *codec)
459{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100460 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +0100461
Takashi Iwai603c4012008-07-30 15:01:44 +0200462 if (!spec)
463 return;
464
465 ad198x_free_kctls(codec);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100466 kfree(spec);
467 snd_hda_detach_beep_device(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200468}
469
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200470static struct hda_codec_ops ad198x_patch_ops = {
471 .build_controls = ad198x_build_controls,
472 .build_pcms = ad198x_build_pcms,
473 .init = ad198x_init,
474 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200475#ifdef CONFIG_SND_HDA_POWER_SAVE
476 .check_power_status = ad198x_check_power_status,
477#endif
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200478};
479
480
481/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100482 * EAPD control
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100483 * the private value = nid
Takashi Iwai18a815d2006-03-01 19:54:39 +0100484 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200485#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100486
487static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
488 struct snd_ctl_elem_value *ucontrol)
489{
490 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
491 struct ad198x_spec *spec = codec->spec;
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100492 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100493 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
494 else
495 ucontrol->value.integer.value[0] = spec->cur_eapd;
496 return 0;
497}
498
499static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
500 struct snd_ctl_elem_value *ucontrol)
501{
502 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
503 struct ad198x_spec *spec = codec->spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +0100504 hda_nid_t nid = kcontrol->private_value & 0xff;
505 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100506 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100507 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100508 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200509 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100510 return 0;
511 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200512 snd_hda_codec_write_cache(codec, nid,
513 0, AC_VERB_SET_EAPD_BTLENABLE,
514 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100515 return 1;
516}
517
Takashi Iwai9230d212006-03-13 13:49:49 +0100518static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
519 struct snd_ctl_elem_info *uinfo);
520static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
521 struct snd_ctl_elem_value *ucontrol);
522static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
523 struct snd_ctl_elem_value *ucontrol);
524
525
Takashi Iwai18a815d2006-03-01 19:54:39 +0100526/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200527 * AD1986A specific
528 */
529
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530#define AD1986A_SPDIF_OUT 0x02
531#define AD1986A_FRONT_DAC 0x03
532#define AD1986A_SURR_DAC 0x04
533#define AD1986A_CLFE_DAC 0x05
534#define AD1986A_ADC 0x06
535
536static hda_nid_t ad1986a_dac_nids[3] = {
537 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
538};
Takashi Iwai985be542005-11-02 18:26:49 +0100539static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +0100540static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541
542static struct hda_input_mux ad1986a_capture_source = {
543 .num_items = 7,
544 .items = {
545 { "Mic", 0x0 },
546 { "CD", 0x1 },
547 { "Aux", 0x3 },
548 { "Line", 0x4 },
549 { "Mix", 0x5 },
550 { "Mono", 0x6 },
551 { "Phone", 0x7 },
552 },
553};
554
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
Takashi Iwai532d5382007-07-27 19:02:40 +0200556static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
557 .ops = &snd_hda_bind_vol,
558 .values = {
559 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
560 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
561 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
562 0
563 },
564};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
Takashi Iwai532d5382007-07-27 19:02:40 +0200566static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
567 .ops = &snd_hda_bind_sw,
568 .values = {
569 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
570 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
571 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
572 0
573 },
574};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
576/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 * mixers
578 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100579static struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200580 /*
581 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
582 */
583 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
584 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
586 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
587 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
588 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
589 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
590 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
591 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
592 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
593 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
594 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
595 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
596 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
597 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
598 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
599 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
600 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
601 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
602 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100603 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
605 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
606 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
607 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
608 {
609 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
610 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200611 .info = ad198x_mux_enum_info,
612 .get = ad198x_mux_enum_get,
613 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 },
615 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
616 { } /* end */
617};
618
Takashi Iwai9230d212006-03-13 13:49:49 +0100619/* additional mixers for 3stack mode */
620static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
621 {
622 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
623 .name = "Channel Mode",
624 .info = ad198x_ch_mode_info,
625 .get = ad198x_ch_mode_get,
626 .put = ad198x_ch_mode_put,
627 },
628 { } /* end */
629};
630
631/* laptop model - 2ch only */
632static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
633
Takashi Iwai20a45e82007-08-15 22:20:45 +0200634/* master controls both pins 0x1a and 0x1b */
635static struct hda_bind_ctls ad1986a_laptop_master_vol = {
636 .ops = &snd_hda_bind_vol,
637 .values = {
638 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
639 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
640 0,
641 },
642};
643
644static struct hda_bind_ctls ad1986a_laptop_master_sw = {
645 .ops = &snd_hda_bind_sw,
646 .values = {
647 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
648 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
649 0,
650 },
651};
652
Takashi Iwai9230d212006-03-13 13:49:49 +0100653static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
654 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
655 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200656 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
657 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100658 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
659 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
660 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
661 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
662 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
663 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
664 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
665 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100666 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100667 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100668 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
669 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
670 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
671 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
672 {
673 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
674 .name = "Capture Source",
675 .info = ad198x_mux_enum_info,
676 .get = ad198x_mux_enum_get,
677 .put = ad198x_mux_enum_put,
678 },
679 { } /* end */
680};
681
Takashi Iwai825aa972006-03-17 10:50:49 +0100682/* laptop-eapd model - 2ch only */
683
Takashi Iwai825aa972006-03-17 10:50:49 +0100684static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
685 .num_items = 3,
686 .items = {
687 { "Mic", 0x0 },
688 { "Internal Mic", 0x4 },
689 { "Mix", 0x5 },
690 },
691};
692
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100693static struct hda_input_mux ad1986a_automic_capture_source = {
694 .num_items = 2,
695 .items = {
696 { "Mic", 0x0 },
697 { "Mix", 0x5 },
698 },
699};
700
Takashi Iwai16d11a82009-06-24 14:07:53 +0200701static struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200702 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
703 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai16d11a82009-06-24 14:07:53 +0200704 { } /* end */
705};
706
707static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai825aa972006-03-17 10:50:49 +0100708 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
709 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100710 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
711 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
712 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
713 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
714 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
715 {
716 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
717 .name = "Capture Source",
718 .info = ad198x_mux_enum_info,
719 .get = ad198x_mux_enum_get,
720 .put = ad198x_mux_enum_put,
721 },
722 {
723 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
724 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100725 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwai1725b822008-11-21 02:25:48 +0100726 .info = ad198x_eapd_info,
727 .get = ad198x_eapd_get,
728 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100729 .private_value = 0x1b, /* port-D */
Takashi Iwai1725b822008-11-21 02:25:48 +0100730 },
731 { } /* end */
732};
733
Takashi Iwai16d11a82009-06-24 14:07:53 +0200734static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
735 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
736 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
Takashi Iwai825aa972006-03-17 10:50:49 +0100737 { } /* end */
738};
739
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100740/* re-connect the mic boost input according to the jack sensing */
741static void ad1986a_automic(struct hda_codec *codec)
742{
743 unsigned int present;
Takashi Iwaid56757a2009-11-18 08:00:14 +0100744 present = snd_hda_jack_detect(codec, 0x1f);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100745 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
746 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
Takashi Iwaid56757a2009-11-18 08:00:14 +0100747 present ? 0 : 2);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100748}
749
750#define AD1986A_MIC_EVENT 0x36
751
752static void ad1986a_automic_unsol_event(struct hda_codec *codec,
753 unsigned int res)
754{
755 if ((res >> 26) != AD1986A_MIC_EVENT)
756 return;
757 ad1986a_automic(codec);
758}
759
760static int ad1986a_automic_init(struct hda_codec *codec)
761{
762 ad198x_init(codec);
763 ad1986a_automic(codec);
764 return 0;
765}
766
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200767/* laptop-automute - 2ch only */
768
769static void ad1986a_update_hp(struct hda_codec *codec)
770{
771 struct ad198x_spec *spec = codec->spec;
772 unsigned int mute;
773
774 if (spec->jack_present)
775 mute = HDA_AMP_MUTE; /* mute internal speaker */
776 else
777 /* unmute internal speaker if necessary */
778 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
779 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
780 HDA_AMP_MUTE, mute);
781}
782
783static void ad1986a_hp_automute(struct hda_codec *codec)
784{
785 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200786
Takashi Iwaid56757a2009-11-18 08:00:14 +0100787 spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
Takashi Iwai03c405a2009-06-24 14:10:15 +0200788 if (spec->inv_jack_detect)
789 spec->jack_present = !spec->jack_present;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200790 ad1986a_update_hp(codec);
791}
792
793#define AD1986A_HP_EVENT 0x37
794
795static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
796{
797 if ((res >> 26) != AD1986A_HP_EVENT)
798 return;
799 ad1986a_hp_automute(codec);
800}
801
802static int ad1986a_hp_init(struct hda_codec *codec)
803{
804 ad198x_init(codec);
805 ad1986a_hp_automute(codec);
806 return 0;
807}
808
809/* bind hp and internal speaker mute (with plug check) */
810static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
811 struct snd_ctl_elem_value *ucontrol)
812{
813 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
814 long *valp = ucontrol->value.integer.value;
815 int change;
816
817 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
818 HDA_AMP_MUTE,
819 valp[0] ? 0 : HDA_AMP_MUTE);
820 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
821 HDA_AMP_MUTE,
822 valp[1] ? 0 : HDA_AMP_MUTE);
823 if (change)
824 ad1986a_update_hp(codec);
825 return change;
826}
827
Takashi Iwai16d11a82009-06-24 14:07:53 +0200828static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200829 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
830 {
831 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
832 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100833 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200834 .info = snd_hda_mixer_amp_switch_info,
835 .get = snd_hda_mixer_amp_switch_get,
836 .put = ad1986a_hp_master_sw_put,
837 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
838 },
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200839 { } /* end */
840};
841
Takashi Iwai16d11a82009-06-24 14:07:53 +0200842
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843/*
844 * initialization verbs
845 */
846static struct hda_verb ad1986a_init_verbs[] = {
847 /* Front, Surround, CLFE DAC; mute as default */
848 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
849 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
850 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
851 /* Downmix - off */
852 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
853 /* HP, Line-Out, Surround, CLFE selectors */
854 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
855 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
856 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
857 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
858 /* Mono selector */
859 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
860 /* Mic selector: Mic 1/2 pin */
861 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
862 /* Line-in selector: Line-in */
863 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
864 /* Mic 1/2 swap */
865 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
866 /* Record selector: mic */
867 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
868 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
869 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
870 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
871 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
872 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
873 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
874 /* PC beep */
875 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
876 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
877 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
878 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
879 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
880 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
881 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200882 /* HP Pin */
883 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
884 /* Front, Surround, CLFE Pins */
885 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
886 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
887 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
888 /* Mono Pin */
889 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
890 /* Mic Pin */
891 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
892 /* Line, Aux, CD, Beep-In Pin */
893 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
894 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
895 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
896 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
897 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 { } /* end */
899};
900
Takashi Iwai9230d212006-03-13 13:49:49 +0100901static struct hda_verb ad1986a_ch2_init[] = {
902 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200903 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
904 /* Line-in selectors */
905 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100906 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200907 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
908 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
909 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100910 { } /* end */
911};
912
913static struct hda_verb ad1986a_ch4_init[] = {
914 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200915 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
916 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100917 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200918 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
919 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100920 { } /* end */
921};
922
923static struct hda_verb ad1986a_ch6_init[] = {
924 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200925 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
926 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100927 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200928 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
929 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100930 { } /* end */
931};
932
933static struct hda_channel_mode ad1986a_modes[3] = {
934 { 2, ad1986a_ch2_init },
935 { 4, ad1986a_ch4_init },
936 { 6, ad1986a_ch6_init },
937};
938
Takashi Iwai825aa972006-03-17 10:50:49 +0100939/* eapd initialization */
940static struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +0100941 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa972006-03-17 10:50:49 +0100942 {}
943};
944
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100945static struct hda_verb ad1986a_automic_verbs[] = {
946 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
947 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
948 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
949 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
950 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
951 {}
952};
953
Tobin Davisf36090f2007-01-08 11:07:12 +0100954/* Ultra initialization */
955static struct hda_verb ad1986a_ultra_init[] = {
956 /* eapd initialization */
957 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
958 /* CLFE -> Mic in */
959 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
960 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
961 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
962 { } /* end */
963};
964
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200965/* pin sensing on HP jack */
966static struct hda_verb ad1986a_hp_init_verbs[] = {
967 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
968 {}
969};
970
Takashi Iwaic912e7a2009-06-24 14:14:34 +0200971static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
972 unsigned int res)
973{
974 switch (res >> 26) {
975 case AD1986A_HP_EVENT:
976 ad1986a_hp_automute(codec);
977 break;
978 case AD1986A_MIC_EVENT:
979 ad1986a_automic(codec);
980 break;
981 }
982}
983
984static int ad1986a_samsung_p50_init(struct hda_codec *codec)
985{
986 ad198x_init(codec);
987 ad1986a_hp_automute(codec);
988 ad1986a_automic(codec);
989 return 0;
990}
991
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200992
Takashi Iwai9230d212006-03-13 13:49:49 +0100993/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100994enum {
995 AD1986A_6STACK,
996 AD1986A_3STACK,
997 AD1986A_LAPTOP,
998 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200999 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +01001000 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +01001001 AD1986A_SAMSUNG,
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001002 AD1986A_SAMSUNG_P50,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001003 AD1986A_MODELS
1004};
Takashi Iwai9230d212006-03-13 13:49:49 +01001005
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001006static const char *ad1986a_models[AD1986A_MODELS] = {
1007 [AD1986A_6STACK] = "6stack",
1008 [AD1986A_3STACK] = "3stack",
1009 [AD1986A_LAPTOP] = "laptop",
1010 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001011 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +01001012 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +01001013 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001014 [AD1986A_SAMSUNG_P50] = "samsung-p50",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001015};
1016
1017static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
1018 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001019 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001020 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001021 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001022 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1023 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1024 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1025 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001026 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001027 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001028 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1029 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1030 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1031 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1032 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001033 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Takashi Iwai7db756f2007-12-24 14:36:09 +01001034 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
Tobin Davis18768992007-03-12 22:20:51 +01001035 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001036 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001037 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
Tobin Davisf36090f2007-01-08 11:07:12 +01001038 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001039 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001040 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001041 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001042 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001043 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001044 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001045 {}
1046};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047
Takashi Iwaicb53c622007-08-10 17:21:45 +02001048#ifdef CONFIG_SND_HDA_POWER_SAVE
1049static struct hda_amp_list ad1986a_loopbacks[] = {
1050 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1051 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1052 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1053 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1054 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1055 { } /* end */
1056};
1057#endif
1058
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001059static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1060{
Takashi Iwai2f334f92009-02-20 14:37:42 +01001061 unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001062 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1063}
1064
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065static int patch_ad1986a(struct hda_codec *codec)
1066{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001067 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001068 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001070 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 if (spec == NULL)
1072 return -ENOMEM;
1073
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 codec->spec = spec;
1075
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001076 err = snd_hda_attach_beep_device(codec, 0x19);
1077 if (err < 0) {
1078 ad198x_free(codec);
1079 return err;
1080 }
1081 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1082
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 spec->multiout.max_channels = 6;
1084 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1085 spec->multiout.dac_nids = ad1986a_dac_nids;
1086 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001087 spec->num_adc_nids = 1;
1088 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001089 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001090 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001091 spec->num_mixers = 1;
1092 spec->mixers[0] = ad1986a_mixers;
1093 spec->num_init_verbs = 1;
1094 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001095#ifdef CONFIG_SND_HDA_POWER_SAVE
1096 spec->loopback.amplist = ad1986a_loopbacks;
1097#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001098 spec->vmaster_nid = 0x1b;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01001099 spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001101 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102
Takashi Iwai9230d212006-03-13 13:49:49 +01001103 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001104 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1105 ad1986a_models,
1106 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001107 switch (board_config) {
1108 case AD1986A_3STACK:
1109 spec->num_mixers = 2;
1110 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001111 spec->num_init_verbs = 2;
1112 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001113 spec->channel_mode = ad1986a_modes;
1114 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001115 spec->need_dac_fix = 1;
1116 spec->multiout.max_channels = 2;
1117 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001118 break;
1119 case AD1986A_LAPTOP:
1120 spec->mixers[0] = ad1986a_laptop_mixers;
1121 spec->multiout.max_channels = 2;
1122 spec->multiout.num_dacs = 1;
1123 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1124 break;
Takashi Iwai825aa972006-03-17 10:50:49 +01001125 case AD1986A_LAPTOP_EAPD:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001126 spec->num_mixers = 3;
1127 spec->mixers[0] = ad1986a_laptop_master_mixers;
1128 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1129 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001130 spec->num_init_verbs = 2;
1131 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1132 spec->multiout.max_channels = 2;
1133 spec->multiout.num_dacs = 1;
1134 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1135 if (!is_jack_available(codec, 0x25))
1136 spec->multiout.dig_out_nid = 0;
1137 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1138 break;
1139 case AD1986A_SAMSUNG:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001140 spec->num_mixers = 2;
1141 spec->mixers[0] = ad1986a_laptop_master_mixers;
1142 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001143 spec->num_init_verbs = 3;
Takashi Iwai825aa972006-03-17 10:50:49 +01001144 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001145 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa972006-03-17 10:50:49 +01001146 spec->multiout.max_channels = 2;
1147 spec->multiout.num_dacs = 1;
1148 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001149 if (!is_jack_available(codec, 0x25))
1150 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001151 spec->input_mux = &ad1986a_automic_capture_source;
1152 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1153 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa972006-03-17 10:50:49 +01001154 break;
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001155 case AD1986A_SAMSUNG_P50:
1156 spec->num_mixers = 2;
1157 spec->mixers[0] = ad1986a_automute_master_mixers;
1158 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1159 spec->num_init_verbs = 4;
1160 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1161 spec->init_verbs[2] = ad1986a_automic_verbs;
1162 spec->init_verbs[3] = ad1986a_hp_init_verbs;
1163 spec->multiout.max_channels = 2;
1164 spec->multiout.num_dacs = 1;
1165 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1166 if (!is_jack_available(codec, 0x25))
1167 spec->multiout.dig_out_nid = 0;
1168 spec->input_mux = &ad1986a_automic_capture_source;
1169 codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
1170 codec->patch_ops.init = ad1986a_samsung_p50_init;
1171 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001172 case AD1986A_LAPTOP_AUTOMUTE:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001173 spec->num_mixers = 3;
1174 spec->mixers[0] = ad1986a_automute_master_mixers;
1175 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1176 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001177 spec->num_init_verbs = 3;
1178 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1179 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1180 spec->multiout.max_channels = 2;
1181 spec->multiout.num_dacs = 1;
1182 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001183 if (!is_jack_available(codec, 0x25))
1184 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001185 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1186 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1187 codec->patch_ops.init = ad1986a_hp_init;
Takashi Iwai03c405a2009-06-24 14:10:15 +02001188 /* Lenovo N100 seems to report the reversed bit
1189 * for HP jack-sensing
1190 */
1191 spec->inv_jack_detect = 1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001192 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001193 case AD1986A_ULTRA:
1194 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1195 spec->num_init_verbs = 2;
1196 spec->init_verbs[1] = ad1986a_ultra_init;
1197 spec->multiout.max_channels = 2;
1198 spec->multiout.num_dacs = 1;
1199 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1200 spec->multiout.dig_out_nid = 0;
1201 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001202 }
1203
Takashi Iwaid29240c2007-10-26 12:35:56 +02001204 /* AD1986A has a hardware problem that it can't share a stream
1205 * with multiple output pins. The copy of front to surrounds
1206 * causes noisy or silent outputs at a certain timing, e.g.
1207 * changing the volume.
1208 * So, let's disable the shared stream.
1209 */
1210 spec->multiout.no_share_stream = 1;
1211
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 return 0;
1213}
1214
1215/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001216 * AD1983 specific
1217 */
1218
1219#define AD1983_SPDIF_OUT 0x02
1220#define AD1983_DAC 0x03
1221#define AD1983_ADC 0x04
1222
1223static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001224static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001225static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001226
1227static struct hda_input_mux ad1983_capture_source = {
1228 .num_items = 4,
1229 .items = {
1230 { "Mic", 0x0 },
1231 { "Line", 0x1 },
1232 { "Mix", 0x2 },
1233 { "Mix Mono", 0x3 },
1234 },
1235};
1236
1237/*
1238 * SPDIF playback route
1239 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001240static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001241{
1242 static char *texts[] = { "PCM", "ADC" };
1243
1244 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1245 uinfo->count = 1;
1246 uinfo->value.enumerated.items = 2;
1247 if (uinfo->value.enumerated.item > 1)
1248 uinfo->value.enumerated.item = 1;
1249 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1250 return 0;
1251}
1252
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001253static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001254{
1255 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1256 struct ad198x_spec *spec = codec->spec;
1257
1258 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1259 return 0;
1260}
1261
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001262static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001263{
1264 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1265 struct ad198x_spec *spec = codec->spec;
1266
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001267 if (ucontrol->value.enumerated.item[0] > 1)
1268 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001269 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1270 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001271 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1272 AC_VERB_SET_CONNECT_SEL,
1273 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001274 return 1;
1275 }
1276 return 0;
1277}
1278
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001279static struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001280 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1281 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1282 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1283 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1284 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1285 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1286 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1287 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1288 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1289 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1290 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1291 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001292 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
1293 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1294 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1295 {
1296 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1297 .name = "Capture Source",
1298 .info = ad198x_mux_enum_info,
1299 .get = ad198x_mux_enum_get,
1300 .put = ad198x_mux_enum_put,
1301 },
1302 {
1303 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001304 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001305 .info = ad1983_spdif_route_info,
1306 .get = ad1983_spdif_route_get,
1307 .put = ad1983_spdif_route_put,
1308 },
1309 { } /* end */
1310};
1311
1312static struct hda_verb ad1983_init_verbs[] = {
1313 /* Front, HP, Mono; mute as default */
1314 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1315 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1316 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1317 /* Beep, PCM, Mic, Line-In: mute */
1318 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1319 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1320 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1321 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1322 /* Front, HP selectors; from Mix */
1323 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1324 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1325 /* Mono selector; from Mix */
1326 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1327 /* Mic selector; Mic */
1328 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1329 /* Line-in selector: Line-in */
1330 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1331 /* Mic boost: 0dB */
1332 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1333 /* Record selector: mic */
1334 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1335 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1336 /* SPDIF route: PCM */
1337 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1338 /* Front Pin */
1339 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1340 /* HP Pin */
1341 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1342 /* Mono Pin */
1343 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1344 /* Mic Pin */
1345 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1346 /* Line Pin */
1347 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1348 { } /* end */
1349};
1350
Takashi Iwaicb53c622007-08-10 17:21:45 +02001351#ifdef CONFIG_SND_HDA_POWER_SAVE
1352static struct hda_amp_list ad1983_loopbacks[] = {
1353 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1354 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1355 { } /* end */
1356};
1357#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001358
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001359static int patch_ad1983(struct hda_codec *codec)
1360{
1361 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001362 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001363
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001364 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001365 if (spec == NULL)
1366 return -ENOMEM;
1367
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001368 codec->spec = spec;
1369
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001370 err = snd_hda_attach_beep_device(codec, 0x10);
1371 if (err < 0) {
1372 ad198x_free(codec);
1373 return err;
1374 }
1375 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1376
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001377 spec->multiout.max_channels = 2;
1378 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1379 spec->multiout.dac_nids = ad1983_dac_nids;
1380 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001381 spec->num_adc_nids = 1;
1382 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001383 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001384 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001385 spec->num_mixers = 1;
1386 spec->mixers[0] = ad1983_mixers;
1387 spec->num_init_verbs = 1;
1388 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001389 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001390#ifdef CONFIG_SND_HDA_POWER_SAVE
1391 spec->loopback.amplist = ad1983_loopbacks;
1392#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001393 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001394
1395 codec->patch_ops = ad198x_patch_ops;
1396
1397 return 0;
1398}
1399
1400
1401/*
1402 * AD1981 HD specific
1403 */
1404
1405#define AD1981_SPDIF_OUT 0x02
1406#define AD1981_DAC 0x03
1407#define AD1981_ADC 0x04
1408
1409static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001410static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001411static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001412
1413/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
1414static struct hda_input_mux ad1981_capture_source = {
1415 .num_items = 7,
1416 .items = {
1417 { "Front Mic", 0x0 },
1418 { "Line", 0x1 },
1419 { "Mix", 0x2 },
1420 { "Mix Mono", 0x3 },
1421 { "CD", 0x4 },
1422 { "Mic", 0x6 },
1423 { "Aux", 0x7 },
1424 },
1425};
1426
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001427static struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001428 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1429 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1430 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1431 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1432 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1433 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1434 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1435 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1436 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1437 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1438 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1439 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1440 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1441 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1442 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1443 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1444 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1445 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001446 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
1447 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
1448 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1449 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1450 {
1451 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1452 .name = "Capture Source",
1453 .info = ad198x_mux_enum_info,
1454 .get = ad198x_mux_enum_get,
1455 .put = ad198x_mux_enum_put,
1456 },
1457 /* identical with AD1983 */
1458 {
1459 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001460 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001461 .info = ad1983_spdif_route_info,
1462 .get = ad1983_spdif_route_get,
1463 .put = ad1983_spdif_route_put,
1464 },
1465 { } /* end */
1466};
1467
1468static struct hda_verb ad1981_init_verbs[] = {
1469 /* Front, HP, Mono; mute as default */
1470 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1471 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1472 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1473 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1474 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1475 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1476 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1477 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1478 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1479 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1480 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1481 /* Front, HP selectors; from Mix */
1482 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1483 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1484 /* Mono selector; from Mix */
1485 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1486 /* Mic Mixer; select Front Mic */
1487 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1488 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1489 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001490 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1491 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001492 /* Record selector: Front mic */
1493 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1494 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1495 /* SPDIF route: PCM */
1496 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1497 /* Front Pin */
1498 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1499 /* HP Pin */
1500 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1501 /* Mono Pin */
1502 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1503 /* Front & Rear Mic Pins */
1504 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1505 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1506 /* Line Pin */
1507 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1508 /* Digital Beep */
1509 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1510 /* Line-Out as Input: disabled */
1511 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1512 { } /* end */
1513};
1514
Takashi Iwaicb53c622007-08-10 17:21:45 +02001515#ifdef CONFIG_SND_HDA_POWER_SAVE
1516static struct hda_amp_list ad1981_loopbacks[] = {
1517 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1518 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1519 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1520 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1521 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1522 { } /* end */
1523};
1524#endif
1525
Takashi Iwai18a815d2006-03-01 19:54:39 +01001526/*
1527 * Patch for HP nx6320
1528 *
Tobin Davis18768992007-03-12 22:20:51 +01001529 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001530 * speaker output enabled _and_ mute-LED off.
1531 */
1532
1533#define AD1981_HP_EVENT 0x37
1534#define AD1981_MIC_EVENT 0x38
1535
1536static struct hda_verb ad1981_hp_init_verbs[] = {
1537 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1538 /* pin sensing on HP and Mic jacks */
1539 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1540 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1541 {}
1542};
1543
1544/* turn on/off EAPD (+ mute HP) as a master switch */
1545static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1546 struct snd_ctl_elem_value *ucontrol)
1547{
1548 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1549 struct ad198x_spec *spec = codec->spec;
1550
1551 if (! ad198x_eapd_put(kcontrol, ucontrol))
1552 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001553 /* change speaker pin appropriately */
1554 snd_hda_codec_write(codec, 0x05, 0,
1555 AC_VERB_SET_PIN_WIDGET_CONTROL,
1556 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001557 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001558 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1559 HDA_AMP_MUTE,
1560 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001561 return 1;
1562}
1563
1564/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02001565static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
1566 .ops = &snd_hda_bind_vol,
1567 .values = {
1568 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1569 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1570 0
1571 },
1572};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001573
1574/* mute internal speaker if HP is plugged */
1575static void ad1981_hp_automute(struct hda_codec *codec)
1576{
1577 unsigned int present;
1578
Takashi Iwaid56757a2009-11-18 08:00:14 +01001579 present = snd_hda_jack_detect(codec, 0x06);
Takashi Iwai47fd8302007-08-10 17:11:07 +02001580 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1581 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001582}
1583
1584/* toggle input of built-in and mic jack appropriately */
1585static void ad1981_hp_automic(struct hda_codec *codec)
1586{
1587 static struct hda_verb mic_jack_on[] = {
1588 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1589 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1590 {}
1591 };
1592 static struct hda_verb mic_jack_off[] = {
1593 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1594 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1595 {}
1596 };
1597 unsigned int present;
1598
Takashi Iwaid56757a2009-11-18 08:00:14 +01001599 present = snd_hda_jack_detect(codec, 0x08);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001600 if (present)
1601 snd_hda_sequence_write(codec, mic_jack_on);
1602 else
1603 snd_hda_sequence_write(codec, mic_jack_off);
1604}
1605
1606/* unsolicited event for HP jack sensing */
1607static void ad1981_hp_unsol_event(struct hda_codec *codec,
1608 unsigned int res)
1609{
1610 res >>= 26;
1611 switch (res) {
1612 case AD1981_HP_EVENT:
1613 ad1981_hp_automute(codec);
1614 break;
1615 case AD1981_MIC_EVENT:
1616 ad1981_hp_automic(codec);
1617 break;
1618 }
1619}
1620
1621static struct hda_input_mux ad1981_hp_capture_source = {
1622 .num_items = 3,
1623 .items = {
1624 { "Mic", 0x0 },
1625 { "Docking-Station", 0x1 },
1626 { "Mix", 0x2 },
1627 },
1628};
1629
1630static struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001631 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001632 {
1633 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001634 .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
Takashi Iwai18a815d2006-03-01 19:54:39 +01001635 .name = "Master Playback Switch",
1636 .info = ad198x_eapd_info,
1637 .get = ad198x_eapd_get,
1638 .put = ad1981_hp_master_sw_put,
1639 .private_value = 0x05,
1640 },
1641 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1642 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1643#if 0
1644 /* FIXME: analog mic/line loopback doesn't work with my tests...
1645 * (although recording is OK)
1646 */
1647 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1648 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1649 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1650 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1651 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1652 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1653 /* FIXME: does this laptop have analog CD connection? */
1654 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1655 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1656#endif
1657 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1658 HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
1659 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1660 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1661 {
1662 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1663 .name = "Capture Source",
1664 .info = ad198x_mux_enum_info,
1665 .get = ad198x_mux_enum_get,
1666 .put = ad198x_mux_enum_put,
1667 },
1668 { } /* end */
1669};
1670
1671/* initialize jack-sensing, too */
1672static int ad1981_hp_init(struct hda_codec *codec)
1673{
1674 ad198x_init(codec);
1675 ad1981_hp_automute(codec);
1676 ad1981_hp_automic(codec);
1677 return 0;
1678}
1679
Tobin Davis18768992007-03-12 22:20:51 +01001680/* configuration for Toshiba Laptops */
1681static struct hda_verb ad1981_toshiba_init_verbs[] = {
1682 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1683 /* pin sensing on HP and Mic jacks */
1684 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1685 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1686 {}
1687};
1688
1689static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
1690 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1691 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1692 { }
1693};
1694
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001695/* configuration for Lenovo Thinkpad T60 */
1696static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
1697 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1698 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1699 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1700 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1701 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1702 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1703 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1704 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1705 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1706 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1707 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1708 {
1709 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1710 .name = "Capture Source",
1711 .info = ad198x_mux_enum_info,
1712 .get = ad198x_mux_enum_get,
1713 .put = ad198x_mux_enum_put,
1714 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001715 /* identical with AD1983 */
1716 {
1717 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1718 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1719 .info = ad1983_spdif_route_info,
1720 .get = ad1983_spdif_route_get,
1721 .put = ad1983_spdif_route_put,
1722 },
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001723 { } /* end */
1724};
1725
1726static struct hda_input_mux ad1981_thinkpad_capture_source = {
1727 .num_items = 3,
1728 .items = {
1729 { "Mic", 0x0 },
1730 { "Mix", 0x2 },
1731 { "CD", 0x4 },
1732 },
1733};
1734
Takashi Iwai18a815d2006-03-01 19:54:39 +01001735/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001736enum {
1737 AD1981_BASIC,
1738 AD1981_HP,
1739 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001740 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001741 AD1981_MODELS
1742};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001743
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001744static const char *ad1981_models[AD1981_MODELS] = {
1745 [AD1981_HP] = "hp",
1746 [AD1981_THINKPAD] = "thinkpad",
1747 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001748 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001749};
1750
1751static struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001752 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001753 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001754 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001755 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001756 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001757 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001758 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001759 /* HP nx6320 (reversed SSID, H/W bug) */
1760 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001761 {}
1762};
1763
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001764static int patch_ad1981(struct hda_codec *codec)
1765{
1766 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001767 int err, board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001768
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001769 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001770 if (spec == NULL)
1771 return -ENOMEM;
1772
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001773 codec->spec = spec;
1774
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001775 err = snd_hda_attach_beep_device(codec, 0x10);
1776 if (err < 0) {
1777 ad198x_free(codec);
1778 return err;
1779 }
1780 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
1781
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001782 spec->multiout.max_channels = 2;
1783 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1784 spec->multiout.dac_nids = ad1981_dac_nids;
1785 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001786 spec->num_adc_nids = 1;
1787 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001788 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001789 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001790 spec->num_mixers = 1;
1791 spec->mixers[0] = ad1981_mixers;
1792 spec->num_init_verbs = 1;
1793 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001794 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001795#ifdef CONFIG_SND_HDA_POWER_SAVE
1796 spec->loopback.amplist = ad1981_loopbacks;
1797#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001798 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001799
1800 codec->patch_ops = ad198x_patch_ops;
1801
Takashi Iwai18a815d2006-03-01 19:54:39 +01001802 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001803 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1804 ad1981_models,
1805 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001806 switch (board_config) {
1807 case AD1981_HP:
1808 spec->mixers[0] = ad1981_hp_mixers;
1809 spec->num_init_verbs = 2;
1810 spec->init_verbs[1] = ad1981_hp_init_verbs;
1811 spec->multiout.dig_out_nid = 0;
1812 spec->input_mux = &ad1981_hp_capture_source;
1813
1814 codec->patch_ops.init = ad1981_hp_init;
1815 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1816 break;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001817 case AD1981_THINKPAD:
1818 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001819 spec->input_mux = &ad1981_thinkpad_capture_source;
1820 break;
Tobin Davis18768992007-03-12 22:20:51 +01001821 case AD1981_TOSHIBA:
1822 spec->mixers[0] = ad1981_hp_mixers;
1823 spec->mixers[1] = ad1981_toshiba_mixers;
1824 spec->num_init_verbs = 2;
1825 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1826 spec->multiout.dig_out_nid = 0;
1827 spec->input_mux = &ad1981_hp_capture_source;
1828 codec->patch_ops.init = ad1981_hp_init;
1829 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1830 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001831 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001832 return 0;
1833}
1834
1835
1836/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001837 * AD1988
1838 *
1839 * Output pins and routes
1840 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001841 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001842 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1843 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1844 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1845 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1846 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1847 * port-F 0x16 (mute) <- 0x2a <- 06
1848 * port-G 0x24 (mute) <- 0x27 <- 05
1849 * port-H 0x25 (mute) <- 0x28 <- 0a
1850 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1851 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001852 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1853 * (*) 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 +01001854 *
1855 * Input pins and routes
1856 *
1857 * pin boost mix input # / adc input #
1858 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1859 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1860 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1861 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1862 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1863 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1864 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1865 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1866 *
1867 *
1868 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001869 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001870 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001871 *
1872 * Inputs of Analog Mix (0x20)
1873 * 0:Port-B (front mic)
1874 * 1:Port-C/G/H (line-in)
1875 * 2:Port-A
1876 * 3:Port-D (line-in/2)
1877 * 4:Port-E/G/H (mic-in)
1878 * 5:Port-F (mic2-in)
1879 * 6:CD
1880 * 7:Beep
1881 *
1882 * ADC selection
1883 * 0:Port-A
1884 * 1:Port-B (front mic-in)
1885 * 2:Port-C (line-in)
1886 * 3:Port-F (mic2-in)
1887 * 4:Port-E (mic-in)
1888 * 5:CD
1889 * 6:Port-G
1890 * 7:Port-H
1891 * 8:Port-D (line-in/2)
1892 * 9:Mix
1893 *
1894 * Proposed pin assignments by the datasheet
1895 *
1896 * 6-stack
1897 * Port-A front headphone
1898 * B front mic-in
1899 * C rear line-in
1900 * D rear front-out
1901 * E rear mic-in
1902 * F rear surround
1903 * G rear CLFE
1904 * H rear side
1905 *
1906 * 3-stack
1907 * Port-A front headphone
1908 * B front mic
1909 * C rear line-in/surround
1910 * D rear front-out
1911 * E rear mic-in/CLFE
1912 *
1913 * laptop
1914 * Port-A headphone
1915 * B mic-in
1916 * C docking station
1917 * D internal speaker (with EAPD)
1918 * E/F quad mic array
1919 */
1920
1921
1922/* models */
1923enum {
1924 AD1988_6STACK,
1925 AD1988_6STACK_DIG,
1926 AD1988_3STACK,
1927 AD1988_3STACK_DIG,
1928 AD1988_LAPTOP,
1929 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01001930 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001931 AD1988_MODEL_LAST,
1932};
1933
Takashi Iwaid32410b12005-11-24 16:06:23 +01001934/* reivision id to check workarounds */
1935#define AD1988A_REV2 0x100200
1936
Takashi Iwai1a806f42006-07-03 15:58:16 +02001937#define is_rev2(codec) \
1938 ((codec)->vendor_id == 0x11d41988 && \
1939 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001940
1941/*
1942 * mixers
1943 */
1944
Takashi Iwaid32410b12005-11-24 16:06:23 +01001945static hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001946 0x04, 0x06, 0x05, 0x0a
1947};
1948
Takashi Iwaid32410b12005-11-24 16:06:23 +01001949static hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001950 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01001951};
1952
1953/* for AD1988A revision-2, DAC2-4 are swapped */
1954static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
1955 0x04, 0x05, 0x0a, 0x06
1956};
1957
1958static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001959 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01001960};
1961
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001962static hda_nid_t ad1988_adc_nids[3] = {
1963 0x08, 0x09, 0x0f
1964};
1965
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001966static hda_nid_t ad1988_capsrc_nids[3] = {
1967 0x0c, 0x0d, 0x0e
1968};
1969
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001970#define AD1988_SPDIF_OUT 0x02
1971#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001972#define AD1988_SPDIF_IN 0x07
1973
Takashi Iwai3a08e302009-02-13 11:37:08 +01001974static hda_nid_t ad1989b_slave_dig_outs[] = {
1975 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001976};
1977
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001978static struct hda_input_mux ad1988_6stack_capture_source = {
1979 .num_items = 5,
1980 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001981 { "Front Mic", 0x1 }, /* port-B */
1982 { "Line", 0x2 }, /* port-C */
1983 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001984 { "CD", 0x5 },
1985 { "Mix", 0x9 },
1986 },
1987};
1988
1989static struct hda_input_mux ad1988_laptop_capture_source = {
1990 .num_items = 3,
1991 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001992 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001993 { "CD", 0x5 },
1994 { "Mix", 0x9 },
1995 },
1996};
1997
1998/*
1999 */
2000static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
2001 struct snd_ctl_elem_info *uinfo)
2002{
2003 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2004 struct ad198x_spec *spec = codec->spec;
2005 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
2006 spec->num_channel_mode);
2007}
2008
2009static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
2010 struct snd_ctl_elem_value *ucontrol)
2011{
2012 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2013 struct ad198x_spec *spec = codec->spec;
2014 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
2015 spec->num_channel_mode, spec->multiout.max_channels);
2016}
2017
2018static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
2019 struct snd_ctl_elem_value *ucontrol)
2020{
2021 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2022 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002023 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
2024 spec->num_channel_mode,
2025 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02002026 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02002027 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002028 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002029}
2030
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002031/* 6-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002032static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002033 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2034 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
2035 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2036 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
2037 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002038 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002039};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002040
Takashi Iwaid32410b12005-11-24 16:06:23 +01002041static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
2042 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2043 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2044 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2045 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2046 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002047 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002048};
2049
2050static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002051 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2052 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2053 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2054 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2055 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2056 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2057 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2058
2059 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2060 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2061 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2062 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2063 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2064 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2065 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2066 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2067
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002068 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002069 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2070
2071 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2072 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2073
2074 { } /* end */
2075};
2076
2077/* 3-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002078static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002079 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002080 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002081 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2082 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002083 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002084};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002085
Takashi Iwaid32410b12005-11-24 16:06:23 +01002086static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
2087 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002088 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2089 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2090 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002091 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002092};
2093
2094static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002095 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002096 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2097 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2098 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002099 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2100 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2101
2102 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2103 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2104 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2105 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2106 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2107 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2108 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2109 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2110
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002111 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002112 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2113
2114 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2115 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2116 {
2117 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2118 .name = "Channel Mode",
2119 .info = ad198x_ch_mode_info,
2120 .get = ad198x_ch_mode_get,
2121 .put = ad198x_ch_mode_put,
2122 },
2123
2124 { } /* end */
2125};
2126
2127/* laptop mode */
2128static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
2129 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2130 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2131 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2132
2133 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2134 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2135 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2136 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2137 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2138 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2139
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002140 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002141 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2142
2143 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2144
2145 {
2146 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2147 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002148 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01002149 .info = ad198x_eapd_info,
2150 .get = ad198x_eapd_get,
2151 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01002152 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002153 },
2154
2155 { } /* end */
2156};
2157
2158/* capture */
2159static struct snd_kcontrol_new ad1988_capture_mixers[] = {
2160 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2161 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2162 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2163 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2164 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2165 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2166 {
2167 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2168 /* The multiple "Capture Source" controls confuse alsamixer
2169 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002170 */
2171 /* .name = "Capture Source", */
2172 .name = "Input Source",
2173 .count = 3,
2174 .info = ad198x_mux_enum_info,
2175 .get = ad198x_mux_enum_get,
2176 .put = ad198x_mux_enum_put,
2177 },
2178 { } /* end */
2179};
2180
2181static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2182 struct snd_ctl_elem_info *uinfo)
2183{
2184 static char *texts[] = {
2185 "PCM", "ADC1", "ADC2", "ADC3"
2186 };
2187 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2188 uinfo->count = 1;
2189 uinfo->value.enumerated.items = 4;
2190 if (uinfo->value.enumerated.item >= 4)
2191 uinfo->value.enumerated.item = 3;
2192 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2193 return 0;
2194}
2195
2196static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2197 struct snd_ctl_elem_value *ucontrol)
2198{
2199 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2200 unsigned int sel;
2201
Takashi Iwaibddcf542007-07-24 18:04:05 +02002202 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2203 AC_AMP_GET_INPUT);
2204 if (!(sel & 0x80))
2205 ucontrol->value.enumerated.item[0] = 0;
2206 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002207 sel = snd_hda_codec_read(codec, 0x0b, 0,
2208 AC_VERB_GET_CONNECT_SEL, 0);
2209 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002210 sel++;
2211 else
2212 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002213 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002214 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002215 return 0;
2216}
2217
2218static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2219 struct snd_ctl_elem_value *ucontrol)
2220{
2221 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002222 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002223 int change;
2224
Takashi Iwai35b26722007-05-05 12:17:17 +02002225 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002226 if (val > 3)
2227 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002228 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002229 sel = snd_hda_codec_read(codec, 0x1d, 0,
2230 AC_VERB_GET_AMP_GAIN_MUTE,
2231 AC_AMP_GET_INPUT);
2232 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002233 if (change) {
2234 snd_hda_codec_write_cache(codec, 0x1d, 0,
2235 AC_VERB_SET_AMP_GAIN_MUTE,
2236 AMP_IN_UNMUTE(0));
2237 snd_hda_codec_write_cache(codec, 0x1d, 0,
2238 AC_VERB_SET_AMP_GAIN_MUTE,
2239 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002240 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002241 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002242 sel = snd_hda_codec_read(codec, 0x1d, 0,
2243 AC_VERB_GET_AMP_GAIN_MUTE,
2244 AC_AMP_GET_INPUT | 0x01);
2245 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002246 if (change) {
2247 snd_hda_codec_write_cache(codec, 0x1d, 0,
2248 AC_VERB_SET_AMP_GAIN_MUTE,
2249 AMP_IN_MUTE(0));
2250 snd_hda_codec_write_cache(codec, 0x1d, 0,
2251 AC_VERB_SET_AMP_GAIN_MUTE,
2252 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002253 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002254 sel = snd_hda_codec_read(codec, 0x0b, 0,
2255 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2256 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002257 if (change)
2258 snd_hda_codec_write_cache(codec, 0x0b, 0,
2259 AC_VERB_SET_CONNECT_SEL,
2260 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002261 }
2262 return change;
2263}
2264
2265static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
2266 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2267 {
2268 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2269 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002270 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002271 .info = ad1988_spdif_playback_source_info,
2272 .get = ad1988_spdif_playback_source_get,
2273 .put = ad1988_spdif_playback_source_put,
2274 },
2275 { } /* end */
2276};
2277
2278static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
2279 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2280 { } /* end */
2281};
2282
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002283static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
2284 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002285 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002286 { } /* end */
2287};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002288
2289/*
2290 * initialization verbs
2291 */
2292
2293/*
2294 * for 6-stack (+dig)
2295 */
2296static struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002297 /* Front, Surround, CLFE, side DAC; unmute as default */
2298 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2299 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2300 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2301 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002302 /* Port-A front headphon path */
2303 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2304 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2305 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2306 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2307 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2308 /* Port-D line-out path */
2309 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2310 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2311 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2312 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2313 /* Port-F surround path */
2314 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2315 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2316 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2317 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2318 /* Port-G CLFE path */
2319 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2320 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2321 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2322 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2323 /* Port-H side path */
2324 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2325 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2326 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2327 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2328 /* Mono out path */
2329 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2330 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2331 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2332 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2333 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2334 /* Port-B front mic-in path */
2335 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2336 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2337 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2338 /* Port-C line-in path */
2339 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2340 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2341 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2342 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2343 /* Port-E mic-in path */
2344 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2345 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2346 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2347 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002348 /* Analog CD Input */
2349 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002350 /* Analog Mix output amp */
2351 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002352
2353 { }
2354};
2355
2356static struct hda_verb ad1988_capture_init_verbs[] = {
2357 /* mute analog mix */
2358 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2359 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2360 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2361 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2362 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2363 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2364 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2365 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2366 /* select ADCs - front-mic */
2367 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2368 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2369 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002370
2371 { }
2372};
2373
2374static struct hda_verb ad1988_spdif_init_verbs[] = {
2375 /* SPDIF out sel */
2376 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2377 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2378 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002379 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002380 /* SPDIF out pin */
2381 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002382
2383 { }
2384};
2385
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002386/* AD1989 has no ADC -> SPDIF route */
2387static struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002388 /* SPDIF-1 out pin */
2389 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002390 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002391 /* SPDIF-2/HDMI out pin */
2392 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2393 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002394 { }
2395};
2396
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002397/*
2398 * verbs for 3stack (+dig)
2399 */
2400static struct hda_verb ad1988_3stack_ch2_init[] = {
2401 /* set port-C to line-in */
2402 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2403 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2404 /* set port-E to mic-in */
2405 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2406 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2407 { } /* end */
2408};
2409
2410static struct hda_verb ad1988_3stack_ch6_init[] = {
2411 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002412 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002413 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002414 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002415 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002416 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002417 { } /* end */
2418};
2419
2420static struct hda_channel_mode ad1988_3stack_modes[2] = {
2421 { 2, ad1988_3stack_ch2_init },
2422 { 6, ad1988_3stack_ch6_init },
2423};
2424
2425static struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002426 /* Front, Surround, CLFE, side DAC; unmute as default */
2427 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2428 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2429 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2430 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002431 /* Port-A front headphon path */
2432 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2433 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2434 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2435 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2436 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2437 /* Port-D line-out path */
2438 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2439 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2440 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2441 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2442 /* Mono out path */
2443 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2444 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2445 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2446 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2447 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2448 /* Port-B front mic-in path */
2449 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2450 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2451 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002452 /* Port-C line-in/surround path - 6ch mode as default */
2453 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2454 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002455 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002456 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002457 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002458 /* Port-E mic-in/CLFE path - 6ch mode as default */
2459 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2460 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002461 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002462 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002463 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2464 /* mute analog mix */
2465 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2466 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2467 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2468 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2469 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2470 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2471 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2472 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2473 /* select ADCs - front-mic */
2474 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2475 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2476 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002477 /* Analog Mix output amp */
2478 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002479 { }
2480};
2481
2482/*
2483 * verbs for laptop mode (+dig)
2484 */
2485static struct hda_verb ad1988_laptop_hp_on[] = {
2486 /* unmute port-A and mute port-D */
2487 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2488 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2489 { } /* end */
2490};
2491static struct hda_verb ad1988_laptop_hp_off[] = {
2492 /* mute port-A and unmute port-D */
2493 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2494 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2495 { } /* end */
2496};
2497
2498#define AD1988_HP_EVENT 0x01
2499
2500static struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002501 /* Front, Surround, CLFE, side DAC; unmute as default */
2502 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2503 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2504 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2505 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002506 /* Port-A front headphon path */
2507 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2508 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2509 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2510 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2511 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2512 /* unsolicited event for pin-sense */
2513 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2514 /* Port-D line-out path + EAPD */
2515 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2516 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2517 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2518 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2519 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2520 /* Mono out path */
2521 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2522 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2523 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2524 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2525 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2526 /* Port-B mic-in path */
2527 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2528 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2529 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2530 /* Port-C docking station - try to output */
2531 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2532 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2533 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2534 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2535 /* mute analog mix */
2536 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2537 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2538 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2539 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2540 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2541 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2542 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2543 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2544 /* select ADCs - mic */
2545 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2546 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2547 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002548 /* Analog Mix output amp */
2549 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002550 { }
2551};
2552
2553static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2554{
2555 if ((res >> 26) != AD1988_HP_EVENT)
2556 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01002557 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002558 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2559 else
2560 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2561}
2562
Takashi Iwaicb53c622007-08-10 17:21:45 +02002563#ifdef CONFIG_SND_HDA_POWER_SAVE
2564static struct hda_amp_list ad1988_loopbacks[] = {
2565 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2566 { 0x20, HDA_INPUT, 1 }, /* Line */
2567 { 0x20, HDA_INPUT, 4 }, /* Mic */
2568 { 0x20, HDA_INPUT, 6 }, /* CD */
2569 { } /* end */
2570};
2571#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002572
2573/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002574 * Automatic parse of I/O pins from the BIOS configuration
2575 */
2576
Takashi Iwaid32410b12005-11-24 16:06:23 +01002577enum {
2578 AD_CTL_WIDGET_VOL,
2579 AD_CTL_WIDGET_MUTE,
2580 AD_CTL_BIND_MUTE,
2581};
2582static struct snd_kcontrol_new ad1988_control_templates[] = {
2583 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2584 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2585 HDA_BIND_MUTE(NULL, 0, 0, 0),
2586};
2587
2588/* add dynamic controls */
2589static int add_control(struct ad198x_spec *spec, int type, const char *name,
2590 unsigned long val)
2591{
2592 struct snd_kcontrol_new *knew;
2593
Takashi Iwai603c4012008-07-30 15:01:44 +02002594 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2595 knew = snd_array_new(&spec->kctls);
2596 if (!knew)
2597 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002598 *knew = ad1988_control_templates[type];
2599 knew->name = kstrdup(name, GFP_KERNEL);
2600 if (! knew->name)
2601 return -ENOMEM;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +01002602 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01002603 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002604 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002605 return 0;
2606}
2607
2608#define AD1988_PIN_CD_NID 0x18
2609#define AD1988_PIN_BEEP_NID 0x10
2610
2611static hda_nid_t ad1988_mixer_nids[8] = {
2612 /* A B C D E F G H */
2613 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2614};
2615
2616static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2617{
2618 static hda_nid_t idx_to_dac[8] = {
2619 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002620 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002621 };
2622 static hda_nid_t idx_to_dac_rev2[8] = {
2623 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002624 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002625 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002626 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002627 return idx_to_dac_rev2[idx];
2628 else
2629 return idx_to_dac[idx];
2630}
2631
2632static hda_nid_t ad1988_boost_nids[8] = {
2633 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2634};
2635
2636static int ad1988_pin_idx(hda_nid_t nid)
2637{
2638 static hda_nid_t ad1988_io_pins[8] = {
2639 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2640 };
2641 int i;
2642 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2643 if (ad1988_io_pins[i] == nid)
2644 return i;
2645 return 0; /* should be -1 */
2646}
2647
2648static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2649{
2650 static int loopback_idx[8] = {
2651 2, 0, 1, 3, 4, 5, 1, 4
2652 };
2653 switch (nid) {
2654 case AD1988_PIN_CD_NID:
2655 return 6;
2656 default:
2657 return loopback_idx[ad1988_pin_idx(nid)];
2658 }
2659}
2660
2661static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2662{
2663 static int adc_idx[8] = {
2664 0, 1, 2, 8, 4, 3, 6, 7
2665 };
2666 switch (nid) {
2667 case AD1988_PIN_CD_NID:
2668 return 5;
2669 default:
2670 return adc_idx[ad1988_pin_idx(nid)];
2671 }
2672}
2673
2674/* fill in the dac_nids table from the parsed pin configuration */
2675static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2676 const struct auto_pin_cfg *cfg)
2677{
2678 struct ad198x_spec *spec = codec->spec;
2679 int i, idx;
2680
2681 spec->multiout.dac_nids = spec->private_dac_nids;
2682
2683 /* check the pins hardwired to audio widget */
2684 for (i = 0; i < cfg->line_outs; i++) {
2685 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2686 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2687 }
2688 spec->multiout.num_dacs = cfg->line_outs;
2689 return 0;
2690}
2691
2692/* add playback controls from the parsed DAC table */
2693static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2694 const struct auto_pin_cfg *cfg)
2695{
2696 char name[32];
2697 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2698 hda_nid_t nid;
2699 int i, err;
2700
2701 for (i = 0; i < cfg->line_outs; i++) {
2702 hda_nid_t dac = spec->multiout.dac_nids[i];
2703 if (! dac)
2704 continue;
2705 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2706 if (i == 2) {
2707 /* Center/LFE */
2708 err = add_control(spec, AD_CTL_WIDGET_VOL,
2709 "Center Playback Volume",
2710 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2711 if (err < 0)
2712 return err;
2713 err = add_control(spec, AD_CTL_WIDGET_VOL,
2714 "LFE Playback Volume",
2715 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2716 if (err < 0)
2717 return err;
2718 err = add_control(spec, AD_CTL_BIND_MUTE,
2719 "Center Playback Switch",
2720 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2721 if (err < 0)
2722 return err;
2723 err = add_control(spec, AD_CTL_BIND_MUTE,
2724 "LFE Playback Switch",
2725 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2726 if (err < 0)
2727 return err;
2728 } else {
2729 sprintf(name, "%s Playback Volume", chname[i]);
2730 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2731 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2732 if (err < 0)
2733 return err;
2734 sprintf(name, "%s Playback Switch", chname[i]);
2735 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2736 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2737 if (err < 0)
2738 return err;
2739 }
2740 }
2741 return 0;
2742}
2743
2744/* add playback controls for speaker and HP outputs */
2745static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2746 const char *pfx)
2747{
2748 struct ad198x_spec *spec = codec->spec;
2749 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02002750 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002751 char name[32];
2752
2753 if (! pin)
2754 return 0;
2755
2756 idx = ad1988_pin_idx(pin);
2757 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02002758 /* check whether the corresponding DAC was already taken */
2759 for (i = 0; i < spec->autocfg.line_outs; i++) {
2760 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2761 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
2762 if (dac == nid)
2763 break;
2764 }
2765 if (i >= spec->autocfg.line_outs) {
2766 /* specify the DAC as the extra output */
2767 if (!spec->multiout.hp_nid)
2768 spec->multiout.hp_nid = nid;
2769 else
2770 spec->multiout.extra_out_nid[0] = nid;
2771 /* control HP volume/switch on the output mixer amp */
2772 sprintf(name, "%s Playback Volume", pfx);
2773 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2774 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2775 if (err < 0)
2776 return err;
2777 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01002778 nid = ad1988_mixer_nids[idx];
2779 sprintf(name, "%s Playback Switch", pfx);
2780 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2781 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2782 return err;
2783 return 0;
2784}
2785
2786/* create input playback/capture controls for the given pin */
2787static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2788 const char *ctlname, int boost)
2789{
2790 char name[32];
2791 int err, idx;
2792
2793 sprintf(name, "%s Playback Volume", ctlname);
2794 idx = ad1988_pin_to_loopback_idx(pin);
2795 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2796 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2797 return err;
2798 sprintf(name, "%s Playback Switch", ctlname);
2799 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2800 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2801 return err;
2802 if (boost) {
2803 hda_nid_t bnid;
2804 idx = ad1988_pin_idx(pin);
2805 bnid = ad1988_boost_nids[idx];
2806 if (bnid) {
2807 sprintf(name, "%s Boost", ctlname);
2808 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2809 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2810
2811 }
2812 }
2813 return 0;
2814}
2815
2816/* create playback/capture controls for input pins */
2817static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2818 const struct auto_pin_cfg *cfg)
2819{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002820 struct hda_input_mux *imux = &spec->private_imux;
2821 int i, err;
2822
2823 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002824 err = new_analog_input(spec, cfg->input_pins[i],
2825 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002826 i <= AUTO_PIN_FRONT_MIC);
2827 if (err < 0)
2828 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002829 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002830 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2831 imux->num_items++;
2832 }
2833 imux->items[imux->num_items].label = "Mix";
2834 imux->items[imux->num_items].index = 9;
2835 imux->num_items++;
2836
2837 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2838 "Analog Mix Playback Volume",
2839 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2840 return err;
2841 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2842 "Analog Mix Playback Switch",
2843 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2844 return err;
2845
2846 return 0;
2847}
2848
2849static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2850 hda_nid_t nid, int pin_type,
2851 int dac_idx)
2852{
2853 /* set as output */
2854 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2855 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2856 switch (nid) {
2857 case 0x11: /* port-A - DAC 04 */
2858 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2859 break;
2860 case 0x14: /* port-B - DAC 06 */
2861 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2862 break;
2863 case 0x15: /* port-C - DAC 05 */
2864 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2865 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002866 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002867 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2868 break;
2869 case 0x13: /* mono - DAC 04 */
2870 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2871 break;
2872 }
2873}
2874
2875static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2876{
2877 struct ad198x_spec *spec = codec->spec;
2878 int i;
2879
2880 for (i = 0; i < spec->autocfg.line_outs; i++) {
2881 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2882 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2883 }
2884}
2885
2886static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2887{
2888 struct ad198x_spec *spec = codec->spec;
2889 hda_nid_t pin;
2890
Takashi Iwai82bc9552006-03-21 11:24:42 +01002891 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002892 if (pin) /* connect to front */
2893 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002894 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002895 if (pin) /* connect to front */
2896 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2897}
2898
2899static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2900{
2901 struct ad198x_spec *spec = codec->spec;
2902 int i, idx;
2903
2904 for (i = 0; i < AUTO_PIN_LAST; i++) {
2905 hda_nid_t nid = spec->autocfg.input_pins[i];
2906 if (! nid)
2907 continue;
2908 switch (nid) {
2909 case 0x15: /* port-C */
2910 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2911 break;
2912 case 0x17: /* port-E */
2913 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2914 break;
2915 }
2916 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2917 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2918 if (nid != AD1988_PIN_CD_NID)
2919 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
2920 AMP_OUT_MUTE);
2921 idx = ad1988_pin_idx(nid);
2922 if (ad1988_boost_nids[idx])
2923 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
2924 AC_VERB_SET_AMP_GAIN_MUTE,
2925 AMP_OUT_ZERO);
2926 }
2927}
2928
2929/* parse the BIOS configuration and set up the alc_spec */
2930/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
2931static int ad1988_parse_auto_config(struct hda_codec *codec)
2932{
2933 struct ad198x_spec *spec = codec->spec;
2934 int err;
2935
Kailang Yangdf694da2005-12-05 19:42:22 +01002936 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002937 return err;
2938 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
2939 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002940 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002941 return 0; /* can't find valid BIOS pin config */
2942 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01002943 (err = ad1988_auto_create_extra_out(codec,
2944 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002945 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002946 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002947 "Headphone")) < 0 ||
2948 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
2949 return err;
2950
2951 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2952
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002953 if (spec->autocfg.dig_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002954 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2955 if (spec->autocfg.dig_in_pin)
2956 spec->dig_in_nid = AD1988_SPDIF_IN;
2957
Takashi Iwai603c4012008-07-30 15:01:44 +02002958 if (spec->kctls.list)
2959 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002960
2961 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
2962
2963 spec->input_mux = &spec->private_imux;
2964
2965 return 1;
2966}
2967
2968/* init callback for auto-configuration model -- overriding the default init */
2969static int ad1988_auto_init(struct hda_codec *codec)
2970{
2971 ad198x_init(codec);
2972 ad1988_auto_init_multi_out(codec);
2973 ad1988_auto_init_extra_out(codec);
2974 ad1988_auto_init_analog_input(codec);
2975 return 0;
2976}
2977
2978
2979/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002980 */
2981
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002982static const char *ad1988_models[AD1988_MODEL_LAST] = {
2983 [AD1988_6STACK] = "6stack",
2984 [AD1988_6STACK_DIG] = "6stack-dig",
2985 [AD1988_3STACK] = "3stack",
2986 [AD1988_3STACK_DIG] = "3stack-dig",
2987 [AD1988_LAPTOP] = "laptop",
2988 [AD1988_LAPTOP_DIG] = "laptop-dig",
2989 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002990};
2991
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002992static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002993 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002994 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02002995 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07002996 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002997 {}
2998};
2999
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003000static int patch_ad1988(struct hda_codec *codec)
3001{
3002 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003003 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003004
3005 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3006 if (spec == NULL)
3007 return -ENOMEM;
3008
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003009 codec->spec = spec;
3010
Takashi Iwai1a806f42006-07-03 15:58:16 +02003011 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003012 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
3013
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003014 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003015 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003016 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02003017 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3018 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003019 board_config = AD1988_AUTO;
3020 }
3021
3022 if (board_config == AD1988_AUTO) {
3023 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003024 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003025 if (err < 0) {
3026 ad198x_free(codec);
3027 return err;
3028 } else if (! err) {
3029 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
3030 board_config = AD1988_6STACK;
3031 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003032 }
3033
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003034 err = snd_hda_attach_beep_device(codec, 0x10);
3035 if (err < 0) {
3036 ad198x_free(codec);
3037 return err;
3038 }
3039 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3040
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003041 switch (board_config) {
3042 case AD1988_6STACK:
3043 case AD1988_6STACK_DIG:
3044 spec->multiout.max_channels = 8;
3045 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003046 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003047 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3048 else
3049 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003050 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003051 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003052 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003053 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3054 else
3055 spec->mixers[0] = ad1988_6stack_mixers1;
3056 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003057 spec->num_init_verbs = 1;
3058 spec->init_verbs[0] = ad1988_6stack_init_verbs;
3059 if (board_config == AD1988_6STACK_DIG) {
3060 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3061 spec->dig_in_nid = AD1988_SPDIF_IN;
3062 }
3063 break;
3064 case AD1988_3STACK:
3065 case AD1988_3STACK_DIG:
3066 spec->multiout.max_channels = 6;
3067 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003068 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003069 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3070 else
3071 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003072 spec->input_mux = &ad1988_6stack_capture_source;
3073 spec->channel_mode = ad1988_3stack_modes;
3074 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003075 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003076 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003077 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3078 else
3079 spec->mixers[0] = ad1988_3stack_mixers1;
3080 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003081 spec->num_init_verbs = 1;
3082 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3083 if (board_config == AD1988_3STACK_DIG)
3084 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3085 break;
3086 case AD1988_LAPTOP:
3087 case AD1988_LAPTOP_DIG:
3088 spec->multiout.max_channels = 2;
3089 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003090 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003091 spec->input_mux = &ad1988_laptop_capture_source;
3092 spec->num_mixers = 1;
3093 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01003094 spec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003095 spec->num_init_verbs = 1;
3096 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3097 if (board_config == AD1988_LAPTOP_DIG)
3098 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3099 break;
3100 }
3101
Takashi Iwaid32410b12005-11-24 16:06:23 +01003102 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3103 spec->adc_nids = ad1988_adc_nids;
3104 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003105 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3106 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3107 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003108 if (codec->vendor_id >= 0x11d4989a) {
3109 spec->mixers[spec->num_mixers++] =
3110 ad1989_spdif_out_mixers;
3111 spec->init_verbs[spec->num_init_verbs++] =
3112 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003113 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003114 } else {
3115 spec->mixers[spec->num_mixers++] =
3116 ad1988_spdif_out_mixers;
3117 spec->init_verbs[spec->num_init_verbs++] =
3118 ad1988_spdif_init_verbs;
3119 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003120 }
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003121 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003122 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
3123
3124 codec->patch_ops = ad198x_patch_ops;
3125 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003126 case AD1988_AUTO:
3127 codec->patch_ops.init = ad1988_auto_init;
3128 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003129 case AD1988_LAPTOP:
3130 case AD1988_LAPTOP_DIG:
3131 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3132 break;
3133 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003134#ifdef CONFIG_SND_HDA_POWER_SAVE
3135 spec->loopback.amplist = ad1988_loopbacks;
3136#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003137 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003138
3139 return 0;
3140}
3141
3142
3143/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003144 * AD1884 / AD1984
3145 *
3146 * port-B - front line/mic-in
3147 * port-E - aux in/out
3148 * port-F - aux in/out
3149 * port-C - rear line/mic-in
3150 * port-D - rear line/hp-out
3151 * port-A - front line/hp-out
3152 *
3153 * AD1984 = AD1884 + two digital mic-ins
3154 *
3155 * FIXME:
3156 * For simplicity, we share the single DAC for both HP and line-outs
3157 * right now. The inidividual playbacks could be easily implemented,
3158 * but no build-up framework is given, so far.
3159 */
3160
3161static hda_nid_t ad1884_dac_nids[1] = {
3162 0x04,
3163};
3164
3165static hda_nid_t ad1884_adc_nids[2] = {
3166 0x08, 0x09,
3167};
3168
3169static hda_nid_t ad1884_capsrc_nids[2] = {
3170 0x0c, 0x0d,
3171};
3172
3173#define AD1884_SPDIF_OUT 0x02
3174
3175static struct hda_input_mux ad1884_capture_source = {
3176 .num_items = 4,
3177 .items = {
3178 { "Front Mic", 0x0 },
3179 { "Mic", 0x1 },
3180 { "CD", 0x2 },
3181 { "Mix", 0x3 },
3182 },
3183};
3184
3185static struct snd_kcontrol_new ad1884_base_mixers[] = {
3186 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3187 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3188 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3189 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3190 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3191 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3192 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3193 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3194 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3195 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3196 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3197 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003198 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
3199 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3200 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3201 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3202 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3203 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3204 {
3205 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3206 /* The multiple "Capture Source" controls confuse alsamixer
3207 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003208 */
3209 /* .name = "Capture Source", */
3210 .name = "Input Source",
3211 .count = 2,
3212 .info = ad198x_mux_enum_info,
3213 .get = ad198x_mux_enum_get,
3214 .put = ad198x_mux_enum_put,
3215 },
3216 /* SPDIF controls */
3217 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3218 {
3219 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3220 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3221 /* identical with ad1983 */
3222 .info = ad1983_spdif_route_info,
3223 .get = ad1983_spdif_route_get,
3224 .put = ad1983_spdif_route_put,
3225 },
3226 { } /* end */
3227};
3228
3229static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3230 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3231 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3232 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003233 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003234 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003235 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003236 { } /* end */
3237};
3238
3239/*
3240 * initialization verbs
3241 */
3242static struct hda_verb ad1884_init_verbs[] = {
3243 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003244 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3245 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003246 /* Port-A (HP) mixer */
3247 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3248 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3249 /* Port-A pin */
3250 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3251 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3252 /* HP selector - select DAC2 */
3253 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3254 /* Port-D (Line-out) mixer */
3255 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3256 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3257 /* Port-D pin */
3258 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3259 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3260 /* Mono-out mixer */
3261 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3262 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3263 /* Mono-out pin */
3264 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3265 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3266 /* Mono selector */
3267 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3268 /* Port-B (front mic) pin */
3269 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003270 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003271 /* Port-C (rear mic) pin */
3272 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003273 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003274 /* Analog mixer; mute as default */
3275 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3276 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3277 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3278 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3279 /* Analog Mix output amp */
3280 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3281 /* SPDIF output selector */
3282 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3283 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3284 { } /* end */
3285};
3286
Takashi Iwaicb53c622007-08-10 17:21:45 +02003287#ifdef CONFIG_SND_HDA_POWER_SAVE
3288static struct hda_amp_list ad1884_loopbacks[] = {
3289 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3290 { 0x20, HDA_INPUT, 1 }, /* Mic */
3291 { 0x20, HDA_INPUT, 2 }, /* CD */
3292 { 0x20, HDA_INPUT, 4 }, /* Docking */
3293 { } /* end */
3294};
3295#endif
3296
Takashi Iwai2134ea42008-01-10 16:53:55 +01003297static const char *ad1884_slave_vols[] = {
3298 "PCM Playback Volume",
3299 "Mic Playback Volume",
3300 "Mono Playback Volume",
3301 "Front Mic Playback Volume",
3302 "Mic Playback Volume",
3303 "CD Playback Volume",
3304 "Internal Mic Playback Volume",
Akinobu Mitabca68462009-04-06 18:42:42 +09003305 "Docking Mic Playback Volume",
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003306 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003307 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003308 NULL
3309};
3310
Takashi Iwai2bac6472007-05-18 18:21:41 +02003311static int patch_ad1884(struct hda_codec *codec)
3312{
3313 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003314 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003315
3316 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3317 if (spec == NULL)
3318 return -ENOMEM;
3319
Takashi Iwai2bac6472007-05-18 18:21:41 +02003320 codec->spec = spec;
3321
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003322 err = snd_hda_attach_beep_device(codec, 0x10);
3323 if (err < 0) {
3324 ad198x_free(codec);
3325 return err;
3326 }
3327 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3328
Takashi Iwai2bac6472007-05-18 18:21:41 +02003329 spec->multiout.max_channels = 2;
3330 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3331 spec->multiout.dac_nids = ad1884_dac_nids;
3332 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3333 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3334 spec->adc_nids = ad1884_adc_nids;
3335 spec->capsrc_nids = ad1884_capsrc_nids;
3336 spec->input_mux = &ad1884_capture_source;
3337 spec->num_mixers = 1;
3338 spec->mixers[0] = ad1884_base_mixers;
3339 spec->num_init_verbs = 1;
3340 spec->init_verbs[0] = ad1884_init_verbs;
3341 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003342#ifdef CONFIG_SND_HDA_POWER_SAVE
3343 spec->loopback.amplist = ad1884_loopbacks;
3344#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003345 spec->vmaster_nid = 0x04;
3346 /* we need to cover all playback volumes */
3347 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003348
3349 codec->patch_ops = ad198x_patch_ops;
3350
3351 return 0;
3352}
3353
3354/*
3355 * Lenovo Thinkpad T61/X61
3356 */
3357static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003358 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003359 .items = {
3360 { "Mic", 0x0 },
3361 { "Internal Mic", 0x1 },
3362 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003363 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003364 },
3365};
3366
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003367
3368/*
3369 * Dell Precision T3400
3370 */
3371static struct hda_input_mux ad1984_dell_desktop_capture_source = {
3372 .num_items = 3,
3373 .items = {
3374 { "Front Mic", 0x0 },
3375 { "Line-In", 0x1 },
3376 { "Mix", 0x3 },
3377 },
3378};
3379
3380
Takashi Iwai2bac6472007-05-18 18:21:41 +02003381static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3382 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3383 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3384 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3385 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3386 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3387 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3388 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3389 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3390 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3391 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003392 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3393 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003394 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003395 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3396 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3397 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3398 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3399 {
3400 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3401 /* The multiple "Capture Source" controls confuse alsamixer
3402 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003403 */
3404 /* .name = "Capture Source", */
3405 .name = "Input Source",
3406 .count = 2,
3407 .info = ad198x_mux_enum_info,
3408 .get = ad198x_mux_enum_get,
3409 .put = ad198x_mux_enum_put,
3410 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003411 /* SPDIF controls */
3412 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3413 {
3414 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3415 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3416 /* identical with ad1983 */
3417 .info = ad1983_spdif_route_info,
3418 .get = ad1983_spdif_route_get,
3419 .put = ad1983_spdif_route_put,
3420 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003421 { } /* end */
3422};
3423
3424/* additional verbs */
3425static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3426 /* Port-E (docking station mic) pin */
3427 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3428 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3429 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003430 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003431 /* Analog mixer - docking mic; mute as default */
3432 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003433 /* enable EAPD bit */
3434 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003435 { } /* end */
3436};
3437
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003438/*
3439 * Dell Precision T3400
3440 */
3441static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
3442 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3443 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3444 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3445 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3446 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3447 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3448 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3449 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3450 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003451 HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
3452 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3453 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3454 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3455 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3456 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3457 {
3458 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3459 /* The multiple "Capture Source" controls confuse alsamixer
3460 * So call somewhat different..
3461 */
3462 /* .name = "Capture Source", */
3463 .name = "Input Source",
3464 .count = 2,
3465 .info = ad198x_mux_enum_info,
3466 .get = ad198x_mux_enum_get,
3467 .put = ad198x_mux_enum_put,
3468 },
3469 { } /* end */
3470};
3471
Takashi Iwai2bac6472007-05-18 18:21:41 +02003472/* Digial MIC ADC NID 0x05 + 0x06 */
3473static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3474 struct hda_codec *codec,
3475 unsigned int stream_tag,
3476 unsigned int format,
3477 struct snd_pcm_substream *substream)
3478{
3479 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3480 stream_tag, 0, format);
3481 return 0;
3482}
3483
3484static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3485 struct hda_codec *codec,
3486 struct snd_pcm_substream *substream)
3487{
Takashi Iwai888afa12008-03-18 09:57:50 +01003488 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003489 return 0;
3490}
3491
3492static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3493 .substreams = 2,
3494 .channels_min = 2,
3495 .channels_max = 2,
3496 .nid = 0x05,
3497 .ops = {
3498 .prepare = ad1984_pcm_dmic_prepare,
3499 .cleanup = ad1984_pcm_dmic_cleanup
3500 },
3501};
3502
3503static int ad1984_build_pcms(struct hda_codec *codec)
3504{
3505 struct ad198x_spec *spec = codec->spec;
3506 struct hda_pcm *info;
3507 int err;
3508
3509 err = ad198x_build_pcms(codec);
3510 if (err < 0)
3511 return err;
3512
3513 info = spec->pcm_rec + codec->num_pcms;
3514 codec->num_pcms++;
3515 info->name = "AD1984 Digital Mic";
3516 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3517 return 0;
3518}
3519
3520/* models */
3521enum {
3522 AD1984_BASIC,
3523 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003524 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003525 AD1984_MODELS
3526};
3527
3528static const char *ad1984_models[AD1984_MODELS] = {
3529 [AD1984_BASIC] = "basic",
3530 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003531 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003532};
3533
3534static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3535 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003536 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003537 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003538 {}
3539};
3540
3541static int patch_ad1984(struct hda_codec *codec)
3542{
3543 struct ad198x_spec *spec;
3544 int board_config, err;
3545
3546 err = patch_ad1884(codec);
3547 if (err < 0)
3548 return err;
3549 spec = codec->spec;
3550 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3551 ad1984_models, ad1984_cfg_tbl);
3552 switch (board_config) {
3553 case AD1984_BASIC:
3554 /* additional digital mics */
3555 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3556 codec->patch_ops.build_pcms = ad1984_build_pcms;
3557 break;
3558 case AD1984_THINKPAD:
Jerone Youngebf00c52008-01-07 12:22:18 +01003559 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003560 spec->input_mux = &ad1984_thinkpad_capture_source;
3561 spec->mixers[0] = ad1984_thinkpad_mixers;
3562 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
3563 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003564 case AD1984_DELL_DESKTOP:
3565 spec->multiout.dig_out_nid = 0;
3566 spec->input_mux = &ad1984_dell_desktop_capture_source;
3567 spec->mixers[0] = ad1984_dell_desktop_mixers;
3568 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003569 }
3570 return 0;
3571}
3572
3573
3574/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003575 * AD1883 / AD1884A / AD1984A / AD1984B
3576 *
3577 * port-B (0x14) - front mic-in
3578 * port-E (0x1c) - rear mic-in
3579 * port-F (0x16) - CD / ext out
3580 * port-C (0x15) - rear line-in
3581 * port-D (0x12) - rear line-out
3582 * port-A (0x11) - front hp-out
3583 *
3584 * AD1984A = AD1884A + digital-mic
3585 * AD1883 = equivalent with AD1984A
3586 * AD1984B = AD1984A + extra SPDIF-out
3587 *
3588 * FIXME:
3589 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3590 */
3591
3592static hda_nid_t ad1884a_dac_nids[1] = {
3593 0x03,
3594};
3595
3596#define ad1884a_adc_nids ad1884_adc_nids
3597#define ad1884a_capsrc_nids ad1884_capsrc_nids
3598
3599#define AD1884A_SPDIF_OUT 0x02
3600
3601static struct hda_input_mux ad1884a_capture_source = {
3602 .num_items = 5,
3603 .items = {
3604 { "Front Mic", 0x0 },
3605 { "Mic", 0x4 },
3606 { "Line", 0x1 },
3607 { "CD", 0x2 },
3608 { "Mix", 0x3 },
3609 },
3610};
3611
3612static struct snd_kcontrol_new ad1884a_base_mixers[] = {
3613 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3614 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3615 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3616 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3617 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3618 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3619 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3620 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3621 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3622 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3623 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3624 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3625 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3626 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3627 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3628 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003629 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3630 HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
3631 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3632 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3633 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3634 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3635 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3636 {
3637 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3638 /* The multiple "Capture Source" controls confuse alsamixer
3639 * So call somewhat different..
3640 */
3641 /* .name = "Capture Source", */
3642 .name = "Input Source",
3643 .count = 2,
3644 .info = ad198x_mux_enum_info,
3645 .get = ad198x_mux_enum_get,
3646 .put = ad198x_mux_enum_put,
3647 },
3648 /* SPDIF controls */
3649 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3650 {
3651 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3652 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3653 /* identical with ad1983 */
3654 .info = ad1983_spdif_route_info,
3655 .get = ad1983_spdif_route_get,
3656 .put = ad1983_spdif_route_put,
3657 },
3658 { } /* end */
3659};
3660
3661/*
3662 * initialization verbs
3663 */
3664static struct hda_verb ad1884a_init_verbs[] = {
3665 /* DACs; unmute as default */
3666 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3667 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3668 /* Port-A (HP) mixer - route only from analog mixer */
3669 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3670 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3671 /* Port-A pin */
3672 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3673 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3674 /* Port-D (Line-out) mixer - route only from analog mixer */
3675 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3676 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3677 /* Port-D pin */
3678 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3679 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3680 /* Mono-out mixer - route only from analog mixer */
3681 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3682 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3683 /* Mono-out pin */
3684 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3685 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3686 /* Port-B (front mic) pin */
3687 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003688 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003689 /* Port-C (rear line-in) pin */
3690 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003691 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003692 /* Port-E (rear mic) pin */
3693 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3694 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3695 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
3696 /* Port-F (CD) pin */
3697 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3698 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3699 /* Analog mixer; mute as default */
3700 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3701 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3702 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3703 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3704 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
3705 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3706 /* Analog Mix output amp */
3707 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3708 /* capture sources */
3709 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
3710 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3711 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3712 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3713 /* SPDIF output amp */
3714 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3715 { } /* end */
3716};
3717
3718#ifdef CONFIG_SND_HDA_POWER_SAVE
3719static struct hda_amp_list ad1884a_loopbacks[] = {
3720 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3721 { 0x20, HDA_INPUT, 1 }, /* Mic */
3722 { 0x20, HDA_INPUT, 2 }, /* CD */
3723 { 0x20, HDA_INPUT, 4 }, /* Docking */
3724 { } /* end */
3725};
3726#endif
3727
3728/*
3729 * Laptop model
3730 *
3731 * Port A: Headphone jack
3732 * Port B: MIC jack
3733 * Port C: Internal MIC
3734 * Port D: Dock Line Out (if enabled)
3735 * Port E: Dock Line In (if enabled)
3736 * Port F: Internal speakers
3737 */
3738
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003739static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
3740 struct snd_ctl_elem_value *ucontrol)
3741{
3742 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3743 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
3744 int mute = (!ucontrol->value.integer.value[0] &&
3745 !ucontrol->value.integer.value[1]);
3746 /* toggle GPIO1 according to the mute state */
3747 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
3748 mute ? 0x02 : 0x0);
3749 return ret;
3750}
Takashi Iwaic5059252008-02-16 09:43:56 +01003751
3752static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
3753 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003754 {
3755 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3756 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003757 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003758 .info = snd_hda_mixer_amp_switch_info,
3759 .get = snd_hda_mixer_amp_switch_get,
3760 .put = ad1884a_mobile_master_sw_put,
3761 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3762 },
Takashi Iwaic5059252008-02-16 09:43:56 +01003763 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3764 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3765 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3766 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3767 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3768 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3769 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3770 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3771 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003772 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3773 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
3774 HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3775 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3776 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003777 { } /* end */
3778};
3779
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003780static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
3781 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db17e2009-07-02 16:10:23 +02003782 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
3783 {
3784 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3785 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003786 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai099db17e2009-07-02 16:10:23 +02003787 .info = snd_hda_mixer_amp_switch_info,
3788 .get = snd_hda_mixer_amp_switch_get,
3789 .put = ad1884a_mobile_master_sw_put,
3790 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3791 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003792 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3793 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02003794 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
3795 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003796 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3797 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003798 { } /* end */
3799};
3800
Takashi Iwaic5059252008-02-16 09:43:56 +01003801/* mute internal speaker if HP is plugged */
3802static void ad1884a_hp_automute(struct hda_codec *codec)
3803{
3804 unsigned int present;
3805
Takashi Iwaid56757a2009-11-18 08:00:14 +01003806 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01003807 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3808 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3809 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3810 present ? 0x00 : 0x02);
3811}
3812
Takashi Iwai269ef192008-05-30 15:32:15 +02003813/* switch to external mic if plugged */
3814static void ad1884a_hp_automic(struct hda_codec *codec)
3815{
3816 unsigned int present;
3817
Takashi Iwaid56757a2009-11-18 08:00:14 +01003818 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02003819 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
3820 present ? 0 : 1);
3821}
3822
Takashi Iwaic5059252008-02-16 09:43:56 +01003823#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02003824#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01003825
3826/* unsolicited event for HP jack sensing */
3827static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
3828{
Takashi Iwai269ef192008-05-30 15:32:15 +02003829 switch (res >> 26) {
3830 case AD1884A_HP_EVENT:
3831 ad1884a_hp_automute(codec);
3832 break;
3833 case AD1884A_MIC_EVENT:
3834 ad1884a_hp_automic(codec);
3835 break;
3836 }
Takashi Iwaic5059252008-02-16 09:43:56 +01003837}
3838
3839/* initialize jack-sensing, too */
3840static int ad1884a_hp_init(struct hda_codec *codec)
3841{
3842 ad198x_init(codec);
3843 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02003844 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01003845 return 0;
3846}
3847
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003848/* mute internal speaker if HP or docking HP is plugged */
3849static void ad1884a_laptop_automute(struct hda_codec *codec)
3850{
3851 unsigned int present;
3852
Takashi Iwaid56757a2009-11-18 08:00:14 +01003853 present = snd_hda_jack_detect(codec, 0x11);
3854 if (!present)
3855 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003856 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3857 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3858 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3859 present ? 0x00 : 0x02);
3860}
3861
3862/* switch to external mic if plugged */
3863static void ad1884a_laptop_automic(struct hda_codec *codec)
3864{
3865 unsigned int idx;
3866
Takashi Iwaid56757a2009-11-18 08:00:14 +01003867 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003868 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01003869 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003870 idx = 4;
3871 else
3872 idx = 1;
3873 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
3874}
3875
3876/* unsolicited event for HP jack sensing */
3877static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
3878 unsigned int res)
3879{
3880 switch (res >> 26) {
3881 case AD1884A_HP_EVENT:
3882 ad1884a_laptop_automute(codec);
3883 break;
3884 case AD1884A_MIC_EVENT:
3885 ad1884a_laptop_automic(codec);
3886 break;
3887 }
3888}
3889
3890/* initialize jack-sensing, too */
3891static int ad1884a_laptop_init(struct hda_codec *codec)
3892{
3893 ad198x_init(codec);
3894 ad1884a_laptop_automute(codec);
3895 ad1884a_laptop_automic(codec);
3896 return 0;
3897}
3898
Takashi Iwaic5059252008-02-16 09:43:56 +01003899/* additional verbs for laptop model */
3900static struct hda_verb ad1884a_laptop_verbs[] = {
3901 /* Port-A (HP) pin - always unmuted */
3902 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3903 /* Port-F (int speaker) mixer - route only from analog mixer */
3904 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3905 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08003906 /* Port-F (int speaker) pin */
3907 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01003908 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08003909 /* required for compaq 6530s/6531s speaker output */
3910 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003911 /* Port-C pin - internal mic-in */
3912 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3913 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3914 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02003915 /* Port-D (docking line-out) pin - default unmuted */
3916 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01003917 /* analog mix */
3918 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3919 /* unsolicited event for pin-sense */
3920 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003921 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003922 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003923 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02003924 /* allow to touch GPIO1 (for mute control) */
3925 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
3926 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
3927 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01003928 { } /* end */
3929};
3930
Takashi Iwai73156132009-04-23 08:24:48 +02003931static struct hda_verb ad1884a_mobile_verbs[] = {
3932 /* DACs; unmute as default */
3933 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3934 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3935 /* Port-A (HP) mixer - route only from analog mixer */
3936 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3937 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3938 /* Port-A pin */
3939 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3940 /* Port-A (HP) pin - always unmuted */
3941 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3942 /* Port-B (mic jack) pin */
3943 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3944 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3945 /* Port-C (int mic) pin */
3946 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3947 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3948 /* Port-F (int speaker) mixer - route only from analog mixer */
3949 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3950 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3951 /* Port-F pin */
3952 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3953 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3954 /* Analog mixer; mute as default */
3955 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3956 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3957 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3958 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3959 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3960 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3961 /* Analog Mix output amp */
3962 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3963 /* capture sources */
3964 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
3965 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3966 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3967 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3968 /* unsolicited event for pin-sense */
3969 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
3970 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db17e2009-07-02 16:10:23 +02003971 /* allow to touch GPIO1 (for mute control) */
3972 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
3973 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
3974 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02003975 { } /* end */
3976};
3977
Takashi Iwaic5059252008-02-16 09:43:56 +01003978/*
Takashi Iwaif0813742008-03-18 12:13:03 +01003979 * Thinkpad X300
3980 * 0x11 - HP
3981 * 0x12 - speaker
3982 * 0x14 - mic-in
3983 * 0x17 - built-in mic
3984 */
3985
3986static struct hda_verb ad1984a_thinkpad_verbs[] = {
3987 /* HP unmute */
3988 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3989 /* analog mix */
3990 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3991 /* turn on EAPD */
3992 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
3993 /* unsolicited event for pin-sense */
3994 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
3995 /* internal mic - dmic */
3996 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02003997 /* set magic COEFs for dmic */
3998 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
3999 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01004000 { } /* end */
4001};
4002
4003static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
4004 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4005 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4006 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4007 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4008 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4009 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01004010 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
4011 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4012 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4013 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4014 {
4015 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4016 .name = "Capture Source",
4017 .info = ad198x_mux_enum_info,
4018 .get = ad198x_mux_enum_get,
4019 .put = ad198x_mux_enum_put,
4020 },
4021 { } /* end */
4022};
4023
4024static struct hda_input_mux ad1984a_thinkpad_capture_source = {
4025 .num_items = 3,
4026 .items = {
4027 { "Mic", 0x0 },
4028 { "Internal Mic", 0x5 },
4029 { "Mix", 0x3 },
4030 },
4031};
4032
4033/* mute internal speaker if HP is plugged */
4034static void ad1984a_thinkpad_automute(struct hda_codec *codec)
4035{
4036 unsigned int present;
4037
Takashi Iwaid56757a2009-11-18 08:00:14 +01004038 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01004039 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
4040 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4041}
4042
4043/* unsolicited event for HP jack sensing */
4044static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
4045 unsigned int res)
4046{
4047 if ((res >> 26) != AD1884A_HP_EVENT)
4048 return;
4049 ad1984a_thinkpad_automute(codec);
4050}
4051
4052/* initialize jack-sensing, too */
4053static int ad1984a_thinkpad_init(struct hda_codec *codec)
4054{
4055 ad198x_init(codec);
4056 ad1984a_thinkpad_automute(codec);
4057 return 0;
4058}
4059
4060/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004061 * HP Touchsmart
4062 * port-A (0x11) - front hp-out
4063 * port-B (0x14) - unused
4064 * port-C (0x15) - unused
4065 * port-D (0x12) - rear line out
4066 * port-E (0x1c) - front mic-in
4067 * port-F (0x16) - Internal speakers
4068 * digital-mic (0x17) - Internal mic
4069 */
4070
4071static struct hda_verb ad1984a_touchsmart_verbs[] = {
4072 /* DACs; unmute as default */
4073 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4074 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4075 /* Port-A (HP) mixer - route only from analog mixer */
4076 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4077 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4078 /* Port-A pin */
4079 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4080 /* Port-A (HP) pin - always unmuted */
4081 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4082 /* Port-E (int speaker) mixer - route only from analog mixer */
4083 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4084 /* Port-E pin */
4085 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4086 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4087 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4088 /* Port-F (int speaker) mixer - route only from analog mixer */
4089 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4090 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4091 /* Port-F pin */
4092 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4093 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4094 /* Analog mixer; mute as default */
4095 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4096 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4097 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4098 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4099 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4100 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4101 /* Analog Mix output amp */
4102 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4103 /* capture sources */
4104 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4105 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4106 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4107 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4108 /* unsolicited event for pin-sense */
4109 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4110 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4111 /* allow to touch GPIO1 (for mute control) */
4112 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4113 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4114 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4115 /* internal mic - dmic */
4116 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4117 /* set magic COEFs for dmic */
4118 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4119 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4120 { } /* end */
4121};
4122
4123static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
4124 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4125/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4126 {
4127 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004128 .subdevice = HDA_SUBDEV_AMP_FLAG,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004129 .name = "Master Playback Switch",
4130 .info = snd_hda_mixer_amp_switch_info,
4131 .get = snd_hda_mixer_amp_switch_get,
4132 .put = ad1884a_mobile_master_sw_put,
4133 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4134 },
4135 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4136 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4137 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4138 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4139 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
4140 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4141 { } /* end */
4142};
4143
4144/* switch to external mic if plugged */
4145static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4146{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004147 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004148 snd_hda_codec_write(codec, 0x0c, 0,
4149 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004150 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004151 snd_hda_codec_write(codec, 0x0c, 0,
4152 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004153}
4154
4155
4156/* unsolicited event for HP jack sensing */
4157static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4158 unsigned int res)
4159{
4160 switch (res >> 26) {
4161 case AD1884A_HP_EVENT:
4162 ad1884a_hp_automute(codec);
4163 break;
4164 case AD1884A_MIC_EVENT:
4165 ad1984a_touchsmart_automic(codec);
4166 break;
4167 }
4168}
4169
4170/* initialize jack-sensing, too */
4171static int ad1984a_touchsmart_init(struct hda_codec *codec)
4172{
4173 ad198x_init(codec);
4174 ad1884a_hp_automute(codec);
4175 ad1984a_touchsmart_automic(codec);
4176 return 0;
4177}
4178
4179
4180/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004181 */
4182
4183enum {
4184 AD1884A_DESKTOP,
4185 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004186 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004187 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004188 AD1984A_TOUCHSMART,
Takashi Iwaic5059252008-02-16 09:43:56 +01004189 AD1884A_MODELS
4190};
4191
4192static const char *ad1884a_models[AD1884A_MODELS] = {
4193 [AD1884A_DESKTOP] = "desktop",
4194 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004195 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004196 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004197 [AD1984A_TOUCHSMART] = "touchsmart",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004198};
4199
4200static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
4201 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004202 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004203 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004204 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004205 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004206 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4207 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004208 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004209 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004210 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004211 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004212};
4213
4214static int patch_ad1884a(struct hda_codec *codec)
4215{
4216 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004217 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004218
4219 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4220 if (spec == NULL)
4221 return -ENOMEM;
4222
Takashi Iwaic5059252008-02-16 09:43:56 +01004223 codec->spec = spec;
4224
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004225 err = snd_hda_attach_beep_device(codec, 0x10);
4226 if (err < 0) {
4227 ad198x_free(codec);
4228 return err;
4229 }
4230 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4231
Takashi Iwaic5059252008-02-16 09:43:56 +01004232 spec->multiout.max_channels = 2;
4233 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4234 spec->multiout.dac_nids = ad1884a_dac_nids;
4235 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4236 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4237 spec->adc_nids = ad1884a_adc_nids;
4238 spec->capsrc_nids = ad1884a_capsrc_nids;
4239 spec->input_mux = &ad1884a_capture_source;
4240 spec->num_mixers = 1;
4241 spec->mixers[0] = ad1884a_base_mixers;
4242 spec->num_init_verbs = 1;
4243 spec->init_verbs[0] = ad1884a_init_verbs;
4244 spec->spdif_route = 0;
4245#ifdef CONFIG_SND_HDA_POWER_SAVE
4246 spec->loopback.amplist = ad1884a_loopbacks;
4247#endif
4248 codec->patch_ops = ad198x_patch_ops;
4249
4250 /* override some parameters */
4251 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004252 ad1884a_models,
4253 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01004254 switch (board_config) {
4255 case AD1884A_LAPTOP:
4256 spec->mixers[0] = ad1884a_laptop_mixers;
4257 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4258 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004259 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4260 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004261 /* set the upper-limit for mixer amp to 0dB for avoiding the
4262 * possible damage by overloading
4263 */
4264 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4265 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4266 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4267 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4268 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004269 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004270 case AD1884A_MOBILE:
4271 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004272 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004273 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004274 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4275 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004276 /* set the upper-limit for mixer amp to 0dB for avoiding the
4277 * possible damage by overloading
4278 */
4279 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4280 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4281 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4282 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4283 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004284 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004285 case AD1884A_THINKPAD:
4286 spec->mixers[0] = ad1984a_thinkpad_mixers;
4287 spec->init_verbs[spec->num_init_verbs++] =
4288 ad1984a_thinkpad_verbs;
4289 spec->multiout.dig_out_nid = 0;
4290 spec->input_mux = &ad1984a_thinkpad_capture_source;
4291 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4292 codec->patch_ops.init = ad1984a_thinkpad_init;
4293 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004294 case AD1984A_TOUCHSMART:
4295 spec->mixers[0] = ad1984a_touchsmart_mixers;
4296 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4297 spec->multiout.dig_out_nid = 0;
4298 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4299 codec->patch_ops.init = ad1984a_touchsmart_init;
4300 /* set the upper-limit for mixer amp to 0dB for avoiding the
4301 * possible damage by overloading
4302 */
4303 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4304 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4305 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4306 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4307 (1 << AC_AMPCAP_MUTE_SHIFT));
4308 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004309 }
4310
4311 return 0;
4312}
4313
4314
4315/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004316 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004317 *
4318 * port-A - front hp-out
4319 * port-B - front mic-in
4320 * port-C - rear line-in, shared surr-out (3stack)
4321 * port-D - rear line-out
4322 * port-E - rear mic-in, shared clfe-out (3stack)
4323 * port-F - rear surr-out (6stack)
4324 * port-G - rear clfe-out (6stack)
4325 */
4326
4327static hda_nid_t ad1882_dac_nids[3] = {
4328 0x04, 0x03, 0x05
4329};
4330
4331static hda_nid_t ad1882_adc_nids[2] = {
4332 0x08, 0x09,
4333};
4334
4335static hda_nid_t ad1882_capsrc_nids[2] = {
4336 0x0c, 0x0d,
4337};
4338
4339#define AD1882_SPDIF_OUT 0x02
4340
4341/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
4342static struct hda_input_mux ad1882_capture_source = {
4343 .num_items = 5,
4344 .items = {
4345 { "Front Mic", 0x1 },
4346 { "Mic", 0x4 },
4347 { "Line", 0x2 },
4348 { "CD", 0x3 },
4349 { "Mix", 0x7 },
4350 },
4351};
4352
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004353/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
4354static struct hda_input_mux ad1882a_capture_source = {
4355 .num_items = 5,
4356 .items = {
4357 { "Front Mic", 0x1 },
4358 { "Mic", 0x4},
4359 { "Line", 0x2 },
4360 { "Digital Mic", 0x06 },
4361 { "Mix", 0x7 },
4362 },
4363};
4364
Takashi Iwai0ac85512007-06-20 15:46:13 +02004365static struct snd_kcontrol_new ad1882_base_mixers[] = {
4366 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4367 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4368 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4369 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4370 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4371 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4372 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4373 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004374
Takashi Iwai0ac85512007-06-20 15:46:13 +02004375 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
4376 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
4377 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
4378 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4379 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4380 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4381 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4382 {
4383 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4384 /* The multiple "Capture Source" controls confuse alsamixer
4385 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004386 */
4387 /* .name = "Capture Source", */
4388 .name = "Input Source",
4389 .count = 2,
4390 .info = ad198x_mux_enum_info,
4391 .get = ad198x_mux_enum_get,
4392 .put = ad198x_mux_enum_put,
4393 },
4394 /* SPDIF controls */
4395 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4396 {
4397 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4398 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4399 /* identical with ad1983 */
4400 .info = ad1983_spdif_route_info,
4401 .get = ad1983_spdif_route_get,
4402 .put = ad1983_spdif_route_put,
4403 },
4404 { } /* end */
4405};
4406
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004407static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
4408 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4409 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4410 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4411 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4412 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4413 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4414 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4415 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004416 { } /* end */
4417};
4418
4419static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
4420 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4421 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4422 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4423 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4424 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4425 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4426 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4427 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004428 HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
4429 { } /* end */
4430};
4431
Takashi Iwai0ac85512007-06-20 15:46:13 +02004432static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
4433 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4434 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4435 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4436 {
4437 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4438 .name = "Channel Mode",
4439 .info = ad198x_ch_mode_info,
4440 .get = ad198x_ch_mode_get,
4441 .put = ad198x_ch_mode_put,
4442 },
4443 { } /* end */
4444};
4445
4446static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
4447 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4448 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4449 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4450 { } /* end */
4451};
4452
4453static struct hda_verb ad1882_ch2_init[] = {
4454 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4455 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4456 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4457 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4458 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4459 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4460 { } /* end */
4461};
4462
4463static struct hda_verb ad1882_ch4_init[] = {
4464 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4465 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4466 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4467 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4468 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4469 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4470 { } /* end */
4471};
4472
4473static struct hda_verb ad1882_ch6_init[] = {
4474 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4475 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4476 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4477 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4478 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4479 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4480 { } /* end */
4481};
4482
4483static struct hda_channel_mode ad1882_modes[3] = {
4484 { 2, ad1882_ch2_init },
4485 { 4, ad1882_ch4_init },
4486 { 6, ad1882_ch6_init },
4487};
4488
4489/*
4490 * initialization verbs
4491 */
4492static struct hda_verb ad1882_init_verbs[] = {
4493 /* DACs; mute as default */
4494 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4495 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4496 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4497 /* Port-A (HP) mixer */
4498 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4499 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4500 /* Port-A pin */
4501 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4502 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4503 /* HP selector - select DAC2 */
4504 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4505 /* Port-D (Line-out) mixer */
4506 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4507 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4508 /* Port-D pin */
4509 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4510 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4511 /* Mono-out mixer */
4512 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4513 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4514 /* Mono-out pin */
4515 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4516 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4517 /* Port-B (front mic) pin */
4518 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4519 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4520 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4521 /* Port-C (line-in) pin */
4522 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4523 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4524 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4525 /* Port-C mixer - mute as input */
4526 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4527 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4528 /* Port-E (mic-in) pin */
4529 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4530 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4531 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4532 /* Port-E mixer - mute as input */
4533 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4534 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4535 /* Port-F (surround) */
4536 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4537 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4538 /* Port-G (CLFE) */
4539 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4540 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4541 /* Analog mixer; mute as default */
4542 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4543 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4544 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4545 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4546 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4547 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4548 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4549 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4550 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4551 /* Analog Mix output amp */
4552 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4553 /* SPDIF output selector */
4554 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4555 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4556 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4557 { } /* end */
4558};
4559
Takashi Iwaicb53c622007-08-10 17:21:45 +02004560#ifdef CONFIG_SND_HDA_POWER_SAVE
4561static struct hda_amp_list ad1882_loopbacks[] = {
4562 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4563 { 0x20, HDA_INPUT, 1 }, /* Mic */
4564 { 0x20, HDA_INPUT, 4 }, /* Line */
4565 { 0x20, HDA_INPUT, 6 }, /* CD */
4566 { } /* end */
4567};
4568#endif
4569
Takashi Iwai0ac85512007-06-20 15:46:13 +02004570/* models */
4571enum {
4572 AD1882_3STACK,
4573 AD1882_6STACK,
4574 AD1882_MODELS
4575};
4576
4577static const char *ad1882_models[AD1986A_MODELS] = {
4578 [AD1882_3STACK] = "3stack",
4579 [AD1882_6STACK] = "6stack",
4580};
4581
4582
4583static int patch_ad1882(struct hda_codec *codec)
4584{
4585 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004586 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004587
4588 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4589 if (spec == NULL)
4590 return -ENOMEM;
4591
Takashi Iwai0ac85512007-06-20 15:46:13 +02004592 codec->spec = spec;
4593
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004594 err = snd_hda_attach_beep_device(codec, 0x10);
4595 if (err < 0) {
4596 ad198x_free(codec);
4597 return err;
4598 }
4599 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4600
Takashi Iwai0ac85512007-06-20 15:46:13 +02004601 spec->multiout.max_channels = 6;
4602 spec->multiout.num_dacs = 3;
4603 spec->multiout.dac_nids = ad1882_dac_nids;
4604 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
4605 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
4606 spec->adc_nids = ad1882_adc_nids;
4607 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004608 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004609 spec->input_mux = &ad1882_capture_source;
4610 else
4611 spec->input_mux = &ad1882a_capture_source;
4612 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004613 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004614 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004615 spec->mixers[1] = ad1882_loopback_mixers;
4616 else
4617 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004618 spec->num_init_verbs = 1;
4619 spec->init_verbs[0] = ad1882_init_verbs;
4620 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02004621#ifdef CONFIG_SND_HDA_POWER_SAVE
4622 spec->loopback.amplist = ad1882_loopbacks;
4623#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01004624 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004625
4626 codec->patch_ops = ad198x_patch_ops;
4627
4628 /* override some parameters */
4629 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
4630 ad1882_models, NULL);
4631 switch (board_config) {
4632 default:
4633 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004634 spec->num_mixers = 3;
4635 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004636 spec->channel_mode = ad1882_modes;
4637 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
4638 spec->need_dac_fix = 1;
4639 spec->multiout.max_channels = 2;
4640 spec->multiout.num_dacs = 1;
4641 break;
4642 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004643 spec->num_mixers = 3;
4644 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004645 break;
4646 }
4647 return 0;
4648}
4649
4650
4651/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004652 * patch entries
4653 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004654static struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004655 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02004656 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004657 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004658 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004659 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
4660 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02004661 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
4662 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004663 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004664 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01004665 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02004666 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004667 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02004668 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
4669 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004670 {} /* terminator */
4671};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004672
4673MODULE_ALIAS("snd-hda-codec-id:11d4*");
4674
4675MODULE_LICENSE("GPL");
4676MODULE_DESCRIPTION("Analog Devices HD-audio codec");
4677
4678static struct hda_codec_preset_list analog_list = {
4679 .preset = snd_hda_preset_analog,
4680 .owner = THIS_MODULE,
4681};
4682
4683static int __init patch_analog_init(void)
4684{
4685 return snd_hda_add_codec_preset(&analog_list);
4686}
4687
4688static void __exit patch_analog_exit(void)
4689{
4690 snd_hda_delete_codec_preset(&analog_list);
4691}
4692
4693module_init(patch_analog_init)
4694module_exit(patch_analog_exit)