blob: 92b72d4f3984fd26bc040dc8ae6262eff1755880 [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 Iwai825aa9722006-03-17 10:50:49 +0100682/* laptop-eapd model - 2ch only */
683
Takashi Iwai825aa9722006-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 Iwai825aa9722006-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 Iwai825aa9722006-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 Iwai825aa9722006-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 Iwai825aa9722006-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 Iwai825aa9722006-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 Iwai825aa9722006-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 Iwai825aa9722006-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 Iwai825aa9722006-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;
Daniel T Chen01f59662009-12-13 16:22:58 -05001816 /* set the upper-limit for mixer amp to 0dB for avoiding the
1817 * possible damage by overloading
1818 */
1819 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
1820 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
1821 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
1822 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
1823 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai18a815d2006-03-01 19:54:39 +01001824 break;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001825 case AD1981_THINKPAD:
1826 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001827 spec->input_mux = &ad1981_thinkpad_capture_source;
1828 break;
Tobin Davis18768992007-03-12 22:20:51 +01001829 case AD1981_TOSHIBA:
1830 spec->mixers[0] = ad1981_hp_mixers;
1831 spec->mixers[1] = ad1981_toshiba_mixers;
1832 spec->num_init_verbs = 2;
1833 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1834 spec->multiout.dig_out_nid = 0;
1835 spec->input_mux = &ad1981_hp_capture_source;
1836 codec->patch_ops.init = ad1981_hp_init;
1837 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1838 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001839 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001840 return 0;
1841}
1842
1843
1844/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001845 * AD1988
1846 *
1847 * Output pins and routes
1848 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001849 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001850 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1851 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1852 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1853 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1854 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1855 * port-F 0x16 (mute) <- 0x2a <- 06
1856 * port-G 0x24 (mute) <- 0x27 <- 05
1857 * port-H 0x25 (mute) <- 0x28 <- 0a
1858 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1859 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001860 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1861 * (*) 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 +01001862 *
1863 * Input pins and routes
1864 *
1865 * pin boost mix input # / adc input #
1866 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1867 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1868 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1869 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1870 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1871 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1872 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1873 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1874 *
1875 *
1876 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001877 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001878 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001879 *
1880 * Inputs of Analog Mix (0x20)
1881 * 0:Port-B (front mic)
1882 * 1:Port-C/G/H (line-in)
1883 * 2:Port-A
1884 * 3:Port-D (line-in/2)
1885 * 4:Port-E/G/H (mic-in)
1886 * 5:Port-F (mic2-in)
1887 * 6:CD
1888 * 7:Beep
1889 *
1890 * ADC selection
1891 * 0:Port-A
1892 * 1:Port-B (front mic-in)
1893 * 2:Port-C (line-in)
1894 * 3:Port-F (mic2-in)
1895 * 4:Port-E (mic-in)
1896 * 5:CD
1897 * 6:Port-G
1898 * 7:Port-H
1899 * 8:Port-D (line-in/2)
1900 * 9:Mix
1901 *
1902 * Proposed pin assignments by the datasheet
1903 *
1904 * 6-stack
1905 * Port-A front headphone
1906 * B front mic-in
1907 * C rear line-in
1908 * D rear front-out
1909 * E rear mic-in
1910 * F rear surround
1911 * G rear CLFE
1912 * H rear side
1913 *
1914 * 3-stack
1915 * Port-A front headphone
1916 * B front mic
1917 * C rear line-in/surround
1918 * D rear front-out
1919 * E rear mic-in/CLFE
1920 *
1921 * laptop
1922 * Port-A headphone
1923 * B mic-in
1924 * C docking station
1925 * D internal speaker (with EAPD)
1926 * E/F quad mic array
1927 */
1928
1929
1930/* models */
1931enum {
1932 AD1988_6STACK,
1933 AD1988_6STACK_DIG,
1934 AD1988_3STACK,
1935 AD1988_3STACK_DIG,
1936 AD1988_LAPTOP,
1937 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01001938 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001939 AD1988_MODEL_LAST,
1940};
1941
Takashi Iwaid32410b12005-11-24 16:06:23 +01001942/* reivision id to check workarounds */
1943#define AD1988A_REV2 0x100200
1944
Takashi Iwai1a806f42006-07-03 15:58:16 +02001945#define is_rev2(codec) \
1946 ((codec)->vendor_id == 0x11d41988 && \
1947 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001948
1949/*
1950 * mixers
1951 */
1952
Takashi Iwaid32410b12005-11-24 16:06:23 +01001953static hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001954 0x04, 0x06, 0x05, 0x0a
1955};
1956
Takashi Iwaid32410b12005-11-24 16:06:23 +01001957static hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001958 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01001959};
1960
1961/* for AD1988A revision-2, DAC2-4 are swapped */
1962static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
1963 0x04, 0x05, 0x0a, 0x06
1964};
1965
1966static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001967 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01001968};
1969
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001970static hda_nid_t ad1988_adc_nids[3] = {
1971 0x08, 0x09, 0x0f
1972};
1973
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001974static hda_nid_t ad1988_capsrc_nids[3] = {
1975 0x0c, 0x0d, 0x0e
1976};
1977
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001978#define AD1988_SPDIF_OUT 0x02
1979#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001980#define AD1988_SPDIF_IN 0x07
1981
Takashi Iwai3a08e302009-02-13 11:37:08 +01001982static hda_nid_t ad1989b_slave_dig_outs[] = {
1983 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001984};
1985
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001986static struct hda_input_mux ad1988_6stack_capture_source = {
1987 .num_items = 5,
1988 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001989 { "Front Mic", 0x1 }, /* port-B */
1990 { "Line", 0x2 }, /* port-C */
1991 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001992 { "CD", 0x5 },
1993 { "Mix", 0x9 },
1994 },
1995};
1996
1997static struct hda_input_mux ad1988_laptop_capture_source = {
1998 .num_items = 3,
1999 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002000 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002001 { "CD", 0x5 },
2002 { "Mix", 0x9 },
2003 },
2004};
2005
2006/*
2007 */
2008static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
2009 struct snd_ctl_elem_info *uinfo)
2010{
2011 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2012 struct ad198x_spec *spec = codec->spec;
2013 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
2014 spec->num_channel_mode);
2015}
2016
2017static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
2018 struct snd_ctl_elem_value *ucontrol)
2019{
2020 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2021 struct ad198x_spec *spec = codec->spec;
2022 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
2023 spec->num_channel_mode, spec->multiout.max_channels);
2024}
2025
2026static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
2027 struct snd_ctl_elem_value *ucontrol)
2028{
2029 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2030 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002031 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
2032 spec->num_channel_mode,
2033 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02002034 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02002035 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002036 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002037}
2038
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002039/* 6-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002040static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002041 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2042 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
2043 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2044 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
2045 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002046 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002047};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002048
Takashi Iwaid32410b12005-11-24 16:06:23 +01002049static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
2050 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2051 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2052 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2053 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2054 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002055 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002056};
2057
2058static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002059 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2060 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2061 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2062 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2063 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2064 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2065 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2066
2067 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2068 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2069 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2070 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2071 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2072 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2073 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2074 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2075
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002076 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002077 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2078
2079 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2080 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2081
2082 { } /* end */
2083};
2084
2085/* 3-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002086static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002087 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002088 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002089 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2090 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002091 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002092};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002093
Takashi Iwaid32410b12005-11-24 16:06:23 +01002094static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
2095 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002096 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2097 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2098 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002099 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002100};
2101
2102static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002103 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002104 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2105 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2106 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002107 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2108 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2109
2110 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2111 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2112 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2113 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2114 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2115 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2116 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2117 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2118
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002119 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002120 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2121
2122 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2123 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2124 {
2125 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2126 .name = "Channel Mode",
2127 .info = ad198x_ch_mode_info,
2128 .get = ad198x_ch_mode_get,
2129 .put = ad198x_ch_mode_put,
2130 },
2131
2132 { } /* end */
2133};
2134
2135/* laptop mode */
2136static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
2137 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2138 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2139 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2140
2141 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2142 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2143 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2144 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2145 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2146 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2147
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002148 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002149 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2150
2151 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2152
2153 {
2154 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2155 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002156 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01002157 .info = ad198x_eapd_info,
2158 .get = ad198x_eapd_get,
2159 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01002160 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002161 },
2162
2163 { } /* end */
2164};
2165
2166/* capture */
2167static struct snd_kcontrol_new ad1988_capture_mixers[] = {
2168 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2169 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2170 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2171 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2172 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2173 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2174 {
2175 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2176 /* The multiple "Capture Source" controls confuse alsamixer
2177 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002178 */
2179 /* .name = "Capture Source", */
2180 .name = "Input Source",
2181 .count = 3,
2182 .info = ad198x_mux_enum_info,
2183 .get = ad198x_mux_enum_get,
2184 .put = ad198x_mux_enum_put,
2185 },
2186 { } /* end */
2187};
2188
2189static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2190 struct snd_ctl_elem_info *uinfo)
2191{
2192 static char *texts[] = {
2193 "PCM", "ADC1", "ADC2", "ADC3"
2194 };
2195 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2196 uinfo->count = 1;
2197 uinfo->value.enumerated.items = 4;
2198 if (uinfo->value.enumerated.item >= 4)
2199 uinfo->value.enumerated.item = 3;
2200 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2201 return 0;
2202}
2203
2204static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2205 struct snd_ctl_elem_value *ucontrol)
2206{
2207 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2208 unsigned int sel;
2209
Takashi Iwaibddcf542007-07-24 18:04:05 +02002210 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2211 AC_AMP_GET_INPUT);
2212 if (!(sel & 0x80))
2213 ucontrol->value.enumerated.item[0] = 0;
2214 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002215 sel = snd_hda_codec_read(codec, 0x0b, 0,
2216 AC_VERB_GET_CONNECT_SEL, 0);
2217 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002218 sel++;
2219 else
2220 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002221 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002222 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002223 return 0;
2224}
2225
2226static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2227 struct snd_ctl_elem_value *ucontrol)
2228{
2229 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002230 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002231 int change;
2232
Takashi Iwai35b26722007-05-05 12:17:17 +02002233 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002234 if (val > 3)
2235 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002236 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002237 sel = snd_hda_codec_read(codec, 0x1d, 0,
2238 AC_VERB_GET_AMP_GAIN_MUTE,
2239 AC_AMP_GET_INPUT);
2240 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002241 if (change) {
2242 snd_hda_codec_write_cache(codec, 0x1d, 0,
2243 AC_VERB_SET_AMP_GAIN_MUTE,
2244 AMP_IN_UNMUTE(0));
2245 snd_hda_codec_write_cache(codec, 0x1d, 0,
2246 AC_VERB_SET_AMP_GAIN_MUTE,
2247 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002248 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002249 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002250 sel = snd_hda_codec_read(codec, 0x1d, 0,
2251 AC_VERB_GET_AMP_GAIN_MUTE,
2252 AC_AMP_GET_INPUT | 0x01);
2253 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002254 if (change) {
2255 snd_hda_codec_write_cache(codec, 0x1d, 0,
2256 AC_VERB_SET_AMP_GAIN_MUTE,
2257 AMP_IN_MUTE(0));
2258 snd_hda_codec_write_cache(codec, 0x1d, 0,
2259 AC_VERB_SET_AMP_GAIN_MUTE,
2260 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002261 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002262 sel = snd_hda_codec_read(codec, 0x0b, 0,
2263 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2264 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002265 if (change)
2266 snd_hda_codec_write_cache(codec, 0x0b, 0,
2267 AC_VERB_SET_CONNECT_SEL,
2268 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002269 }
2270 return change;
2271}
2272
2273static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
2274 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2275 {
2276 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2277 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002278 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002279 .info = ad1988_spdif_playback_source_info,
2280 .get = ad1988_spdif_playback_source_get,
2281 .put = ad1988_spdif_playback_source_put,
2282 },
2283 { } /* end */
2284};
2285
2286static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
2287 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2288 { } /* end */
2289};
2290
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002291static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
2292 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002293 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002294 { } /* end */
2295};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002296
2297/*
2298 * initialization verbs
2299 */
2300
2301/*
2302 * for 6-stack (+dig)
2303 */
2304static struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002305 /* Front, Surround, CLFE, side DAC; unmute as default */
2306 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2307 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2308 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2309 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002310 /* Port-A front headphon path */
2311 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2312 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2313 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2314 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2315 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2316 /* Port-D line-out path */
2317 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2318 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2319 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2320 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2321 /* Port-F surround path */
2322 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2323 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2324 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2325 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2326 /* Port-G CLFE path */
2327 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2328 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2329 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2330 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2331 /* Port-H side path */
2332 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2333 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2334 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2335 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2336 /* Mono out path */
2337 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2338 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2339 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2340 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2341 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2342 /* Port-B front mic-in path */
2343 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2344 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2345 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2346 /* Port-C line-in path */
2347 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2348 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2349 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2350 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2351 /* Port-E mic-in path */
2352 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2353 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2354 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2355 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002356 /* Analog CD Input */
2357 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002358 /* Analog Mix output amp */
2359 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002360
2361 { }
2362};
2363
2364static struct hda_verb ad1988_capture_init_verbs[] = {
2365 /* mute analog mix */
2366 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2367 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2368 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2369 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2370 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2371 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2372 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2373 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2374 /* select ADCs - front-mic */
2375 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2376 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2377 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002378
2379 { }
2380};
2381
2382static struct hda_verb ad1988_spdif_init_verbs[] = {
2383 /* SPDIF out sel */
2384 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2385 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2386 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002387 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002388 /* SPDIF out pin */
2389 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002390
2391 { }
2392};
2393
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002394/* AD1989 has no ADC -> SPDIF route */
2395static struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002396 /* SPDIF-1 out pin */
2397 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002398 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002399 /* SPDIF-2/HDMI out pin */
2400 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2401 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002402 { }
2403};
2404
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002405/*
2406 * verbs for 3stack (+dig)
2407 */
2408static struct hda_verb ad1988_3stack_ch2_init[] = {
2409 /* set port-C to line-in */
2410 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2411 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2412 /* set port-E to mic-in */
2413 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2414 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2415 { } /* end */
2416};
2417
2418static struct hda_verb ad1988_3stack_ch6_init[] = {
2419 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002420 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002421 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002422 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002423 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002424 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002425 { } /* end */
2426};
2427
2428static struct hda_channel_mode ad1988_3stack_modes[2] = {
2429 { 2, ad1988_3stack_ch2_init },
2430 { 6, ad1988_3stack_ch6_init },
2431};
2432
2433static struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002434 /* Front, Surround, CLFE, side DAC; unmute as default */
2435 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2436 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2437 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2438 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002439 /* Port-A front headphon path */
2440 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2441 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2442 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2443 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2444 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2445 /* Port-D line-out path */
2446 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2447 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2448 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2449 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2450 /* Mono out path */
2451 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2452 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2453 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2454 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2455 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2456 /* Port-B front mic-in path */
2457 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2458 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2459 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002460 /* Port-C line-in/surround path - 6ch mode as default */
2461 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2462 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002463 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002464 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002465 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002466 /* Port-E mic-in/CLFE path - 6ch mode as default */
2467 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2468 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002469 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002470 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002471 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2472 /* mute analog mix */
2473 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2474 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2475 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2476 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2477 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2478 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2479 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2480 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2481 /* select ADCs - front-mic */
2482 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2483 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2484 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002485 /* Analog Mix output amp */
2486 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002487 { }
2488};
2489
2490/*
2491 * verbs for laptop mode (+dig)
2492 */
2493static struct hda_verb ad1988_laptop_hp_on[] = {
2494 /* unmute port-A and mute port-D */
2495 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2496 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2497 { } /* end */
2498};
2499static struct hda_verb ad1988_laptop_hp_off[] = {
2500 /* mute port-A and unmute port-D */
2501 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2502 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2503 { } /* end */
2504};
2505
2506#define AD1988_HP_EVENT 0x01
2507
2508static struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002509 /* Front, Surround, CLFE, side DAC; unmute as default */
2510 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2511 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2512 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2513 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002514 /* Port-A front headphon path */
2515 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2516 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2517 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2518 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2519 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2520 /* unsolicited event for pin-sense */
2521 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2522 /* Port-D line-out path + EAPD */
2523 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2524 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2525 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2526 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2527 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2528 /* Mono out path */
2529 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2530 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2531 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2532 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2533 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2534 /* Port-B mic-in path */
2535 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2536 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2537 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2538 /* Port-C docking station - try to output */
2539 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2540 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2541 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2542 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2543 /* mute analog mix */
2544 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2545 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2546 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2547 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2548 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2549 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2550 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2551 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2552 /* select ADCs - mic */
2553 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2554 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2555 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002556 /* Analog Mix output amp */
2557 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002558 { }
2559};
2560
2561static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2562{
2563 if ((res >> 26) != AD1988_HP_EVENT)
2564 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01002565 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002566 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2567 else
2568 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2569}
2570
Takashi Iwaicb53c622007-08-10 17:21:45 +02002571#ifdef CONFIG_SND_HDA_POWER_SAVE
2572static struct hda_amp_list ad1988_loopbacks[] = {
2573 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2574 { 0x20, HDA_INPUT, 1 }, /* Line */
2575 { 0x20, HDA_INPUT, 4 }, /* Mic */
2576 { 0x20, HDA_INPUT, 6 }, /* CD */
2577 { } /* end */
2578};
2579#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002580
2581/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002582 * Automatic parse of I/O pins from the BIOS configuration
2583 */
2584
Takashi Iwaid32410b12005-11-24 16:06:23 +01002585enum {
2586 AD_CTL_WIDGET_VOL,
2587 AD_CTL_WIDGET_MUTE,
2588 AD_CTL_BIND_MUTE,
2589};
2590static struct snd_kcontrol_new ad1988_control_templates[] = {
2591 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2592 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2593 HDA_BIND_MUTE(NULL, 0, 0, 0),
2594};
2595
2596/* add dynamic controls */
2597static int add_control(struct ad198x_spec *spec, int type, const char *name,
2598 unsigned long val)
2599{
2600 struct snd_kcontrol_new *knew;
2601
Takashi Iwai603c4012008-07-30 15:01:44 +02002602 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2603 knew = snd_array_new(&spec->kctls);
2604 if (!knew)
2605 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002606 *knew = ad1988_control_templates[type];
2607 knew->name = kstrdup(name, GFP_KERNEL);
2608 if (! knew->name)
2609 return -ENOMEM;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +01002610 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01002611 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002612 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002613 return 0;
2614}
2615
2616#define AD1988_PIN_CD_NID 0x18
2617#define AD1988_PIN_BEEP_NID 0x10
2618
2619static hda_nid_t ad1988_mixer_nids[8] = {
2620 /* A B C D E F G H */
2621 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2622};
2623
2624static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2625{
2626 static hda_nid_t idx_to_dac[8] = {
2627 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002628 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002629 };
2630 static hda_nid_t idx_to_dac_rev2[8] = {
2631 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002632 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002633 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002634 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002635 return idx_to_dac_rev2[idx];
2636 else
2637 return idx_to_dac[idx];
2638}
2639
2640static hda_nid_t ad1988_boost_nids[8] = {
2641 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2642};
2643
2644static int ad1988_pin_idx(hda_nid_t nid)
2645{
2646 static hda_nid_t ad1988_io_pins[8] = {
2647 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2648 };
2649 int i;
2650 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2651 if (ad1988_io_pins[i] == nid)
2652 return i;
2653 return 0; /* should be -1 */
2654}
2655
2656static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2657{
2658 static int loopback_idx[8] = {
2659 2, 0, 1, 3, 4, 5, 1, 4
2660 };
2661 switch (nid) {
2662 case AD1988_PIN_CD_NID:
2663 return 6;
2664 default:
2665 return loopback_idx[ad1988_pin_idx(nid)];
2666 }
2667}
2668
2669static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2670{
2671 static int adc_idx[8] = {
2672 0, 1, 2, 8, 4, 3, 6, 7
2673 };
2674 switch (nid) {
2675 case AD1988_PIN_CD_NID:
2676 return 5;
2677 default:
2678 return adc_idx[ad1988_pin_idx(nid)];
2679 }
2680}
2681
2682/* fill in the dac_nids table from the parsed pin configuration */
2683static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2684 const struct auto_pin_cfg *cfg)
2685{
2686 struct ad198x_spec *spec = codec->spec;
2687 int i, idx;
2688
2689 spec->multiout.dac_nids = spec->private_dac_nids;
2690
2691 /* check the pins hardwired to audio widget */
2692 for (i = 0; i < cfg->line_outs; i++) {
2693 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2694 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2695 }
2696 spec->multiout.num_dacs = cfg->line_outs;
2697 return 0;
2698}
2699
2700/* add playback controls from the parsed DAC table */
2701static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2702 const struct auto_pin_cfg *cfg)
2703{
2704 char name[32];
2705 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2706 hda_nid_t nid;
2707 int i, err;
2708
2709 for (i = 0; i < cfg->line_outs; i++) {
2710 hda_nid_t dac = spec->multiout.dac_nids[i];
2711 if (! dac)
2712 continue;
2713 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2714 if (i == 2) {
2715 /* Center/LFE */
2716 err = add_control(spec, AD_CTL_WIDGET_VOL,
2717 "Center Playback Volume",
2718 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2719 if (err < 0)
2720 return err;
2721 err = add_control(spec, AD_CTL_WIDGET_VOL,
2722 "LFE Playback Volume",
2723 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2724 if (err < 0)
2725 return err;
2726 err = add_control(spec, AD_CTL_BIND_MUTE,
2727 "Center Playback Switch",
2728 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2729 if (err < 0)
2730 return err;
2731 err = add_control(spec, AD_CTL_BIND_MUTE,
2732 "LFE Playback Switch",
2733 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2734 if (err < 0)
2735 return err;
2736 } else {
2737 sprintf(name, "%s Playback Volume", chname[i]);
2738 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2739 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2740 if (err < 0)
2741 return err;
2742 sprintf(name, "%s Playback Switch", chname[i]);
2743 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2744 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2745 if (err < 0)
2746 return err;
2747 }
2748 }
2749 return 0;
2750}
2751
2752/* add playback controls for speaker and HP outputs */
2753static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2754 const char *pfx)
2755{
2756 struct ad198x_spec *spec = codec->spec;
2757 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02002758 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002759 char name[32];
2760
2761 if (! pin)
2762 return 0;
2763
2764 idx = ad1988_pin_idx(pin);
2765 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02002766 /* check whether the corresponding DAC was already taken */
2767 for (i = 0; i < spec->autocfg.line_outs; i++) {
2768 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2769 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
2770 if (dac == nid)
2771 break;
2772 }
2773 if (i >= spec->autocfg.line_outs) {
2774 /* specify the DAC as the extra output */
2775 if (!spec->multiout.hp_nid)
2776 spec->multiout.hp_nid = nid;
2777 else
2778 spec->multiout.extra_out_nid[0] = nid;
2779 /* control HP volume/switch on the output mixer amp */
2780 sprintf(name, "%s Playback Volume", pfx);
2781 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2782 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2783 if (err < 0)
2784 return err;
2785 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01002786 nid = ad1988_mixer_nids[idx];
2787 sprintf(name, "%s Playback Switch", pfx);
2788 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2789 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2790 return err;
2791 return 0;
2792}
2793
2794/* create input playback/capture controls for the given pin */
2795static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2796 const char *ctlname, int boost)
2797{
2798 char name[32];
2799 int err, idx;
2800
2801 sprintf(name, "%s Playback Volume", ctlname);
2802 idx = ad1988_pin_to_loopback_idx(pin);
2803 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2804 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2805 return err;
2806 sprintf(name, "%s Playback Switch", ctlname);
2807 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2808 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2809 return err;
2810 if (boost) {
2811 hda_nid_t bnid;
2812 idx = ad1988_pin_idx(pin);
2813 bnid = ad1988_boost_nids[idx];
2814 if (bnid) {
2815 sprintf(name, "%s Boost", ctlname);
2816 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2817 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2818
2819 }
2820 }
2821 return 0;
2822}
2823
2824/* create playback/capture controls for input pins */
2825static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2826 const struct auto_pin_cfg *cfg)
2827{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002828 struct hda_input_mux *imux = &spec->private_imux;
2829 int i, err;
2830
2831 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002832 err = new_analog_input(spec, cfg->input_pins[i],
2833 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002834 i <= AUTO_PIN_FRONT_MIC);
2835 if (err < 0)
2836 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002837 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002838 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2839 imux->num_items++;
2840 }
2841 imux->items[imux->num_items].label = "Mix";
2842 imux->items[imux->num_items].index = 9;
2843 imux->num_items++;
2844
2845 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2846 "Analog Mix Playback Volume",
2847 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2848 return err;
2849 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2850 "Analog Mix Playback Switch",
2851 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2852 return err;
2853
2854 return 0;
2855}
2856
2857static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2858 hda_nid_t nid, int pin_type,
2859 int dac_idx)
2860{
2861 /* set as output */
2862 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2863 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2864 switch (nid) {
2865 case 0x11: /* port-A - DAC 04 */
2866 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2867 break;
2868 case 0x14: /* port-B - DAC 06 */
2869 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2870 break;
2871 case 0x15: /* port-C - DAC 05 */
2872 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2873 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002874 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002875 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2876 break;
2877 case 0x13: /* mono - DAC 04 */
2878 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2879 break;
2880 }
2881}
2882
2883static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2884{
2885 struct ad198x_spec *spec = codec->spec;
2886 int i;
2887
2888 for (i = 0; i < spec->autocfg.line_outs; i++) {
2889 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2890 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2891 }
2892}
2893
2894static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2895{
2896 struct ad198x_spec *spec = codec->spec;
2897 hda_nid_t pin;
2898
Takashi Iwai82bc9552006-03-21 11:24:42 +01002899 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002900 if (pin) /* connect to front */
2901 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002902 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002903 if (pin) /* connect to front */
2904 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2905}
2906
2907static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2908{
2909 struct ad198x_spec *spec = codec->spec;
2910 int i, idx;
2911
2912 for (i = 0; i < AUTO_PIN_LAST; i++) {
2913 hda_nid_t nid = spec->autocfg.input_pins[i];
2914 if (! nid)
2915 continue;
2916 switch (nid) {
2917 case 0x15: /* port-C */
2918 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2919 break;
2920 case 0x17: /* port-E */
2921 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2922 break;
2923 }
2924 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2925 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2926 if (nid != AD1988_PIN_CD_NID)
2927 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
2928 AMP_OUT_MUTE);
2929 idx = ad1988_pin_idx(nid);
2930 if (ad1988_boost_nids[idx])
2931 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
2932 AC_VERB_SET_AMP_GAIN_MUTE,
2933 AMP_OUT_ZERO);
2934 }
2935}
2936
2937/* parse the BIOS configuration and set up the alc_spec */
2938/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
2939static int ad1988_parse_auto_config(struct hda_codec *codec)
2940{
2941 struct ad198x_spec *spec = codec->spec;
2942 int err;
2943
Kailang Yangdf694da2005-12-05 19:42:22 +01002944 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002945 return err;
2946 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
2947 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002948 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002949 return 0; /* can't find valid BIOS pin config */
2950 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01002951 (err = ad1988_auto_create_extra_out(codec,
2952 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002953 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002954 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002955 "Headphone")) < 0 ||
2956 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
2957 return err;
2958
2959 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2960
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002961 if (spec->autocfg.dig_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002962 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2963 if (spec->autocfg.dig_in_pin)
2964 spec->dig_in_nid = AD1988_SPDIF_IN;
2965
Takashi Iwai603c4012008-07-30 15:01:44 +02002966 if (spec->kctls.list)
2967 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002968
2969 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
2970
2971 spec->input_mux = &spec->private_imux;
2972
2973 return 1;
2974}
2975
2976/* init callback for auto-configuration model -- overriding the default init */
2977static int ad1988_auto_init(struct hda_codec *codec)
2978{
2979 ad198x_init(codec);
2980 ad1988_auto_init_multi_out(codec);
2981 ad1988_auto_init_extra_out(codec);
2982 ad1988_auto_init_analog_input(codec);
2983 return 0;
2984}
2985
2986
2987/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002988 */
2989
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002990static const char *ad1988_models[AD1988_MODEL_LAST] = {
2991 [AD1988_6STACK] = "6stack",
2992 [AD1988_6STACK_DIG] = "6stack-dig",
2993 [AD1988_3STACK] = "3stack",
2994 [AD1988_3STACK_DIG] = "3stack-dig",
2995 [AD1988_LAPTOP] = "laptop",
2996 [AD1988_LAPTOP_DIG] = "laptop-dig",
2997 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002998};
2999
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003000static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01003001 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01003002 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02003003 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07003004 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003005 {}
3006};
3007
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003008static int patch_ad1988(struct hda_codec *codec)
3009{
3010 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003011 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003012
3013 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3014 if (spec == NULL)
3015 return -ENOMEM;
3016
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003017 codec->spec = spec;
3018
Takashi Iwai1a806f42006-07-03 15:58:16 +02003019 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003020 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
3021
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003022 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003023 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003024 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02003025 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3026 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003027 board_config = AD1988_AUTO;
3028 }
3029
3030 if (board_config == AD1988_AUTO) {
3031 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003032 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003033 if (err < 0) {
3034 ad198x_free(codec);
3035 return err;
3036 } else if (! err) {
3037 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
3038 board_config = AD1988_6STACK;
3039 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003040 }
3041
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003042 err = snd_hda_attach_beep_device(codec, 0x10);
3043 if (err < 0) {
3044 ad198x_free(codec);
3045 return err;
3046 }
3047 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3048
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003049 switch (board_config) {
3050 case AD1988_6STACK:
3051 case AD1988_6STACK_DIG:
3052 spec->multiout.max_channels = 8;
3053 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003054 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003055 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3056 else
3057 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003058 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003059 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003060 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003061 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3062 else
3063 spec->mixers[0] = ad1988_6stack_mixers1;
3064 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003065 spec->num_init_verbs = 1;
3066 spec->init_verbs[0] = ad1988_6stack_init_verbs;
3067 if (board_config == AD1988_6STACK_DIG) {
3068 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3069 spec->dig_in_nid = AD1988_SPDIF_IN;
3070 }
3071 break;
3072 case AD1988_3STACK:
3073 case AD1988_3STACK_DIG:
3074 spec->multiout.max_channels = 6;
3075 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003076 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003077 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3078 else
3079 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003080 spec->input_mux = &ad1988_6stack_capture_source;
3081 spec->channel_mode = ad1988_3stack_modes;
3082 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003083 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003084 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003085 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3086 else
3087 spec->mixers[0] = ad1988_3stack_mixers1;
3088 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003089 spec->num_init_verbs = 1;
3090 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3091 if (board_config == AD1988_3STACK_DIG)
3092 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3093 break;
3094 case AD1988_LAPTOP:
3095 case AD1988_LAPTOP_DIG:
3096 spec->multiout.max_channels = 2;
3097 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003098 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003099 spec->input_mux = &ad1988_laptop_capture_source;
3100 spec->num_mixers = 1;
3101 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01003102 spec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003103 spec->num_init_verbs = 1;
3104 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3105 if (board_config == AD1988_LAPTOP_DIG)
3106 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3107 break;
3108 }
3109
Takashi Iwaid32410b12005-11-24 16:06:23 +01003110 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3111 spec->adc_nids = ad1988_adc_nids;
3112 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003113 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3114 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3115 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003116 if (codec->vendor_id >= 0x11d4989a) {
3117 spec->mixers[spec->num_mixers++] =
3118 ad1989_spdif_out_mixers;
3119 spec->init_verbs[spec->num_init_verbs++] =
3120 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003121 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003122 } else {
3123 spec->mixers[spec->num_mixers++] =
3124 ad1988_spdif_out_mixers;
3125 spec->init_verbs[spec->num_init_verbs++] =
3126 ad1988_spdif_init_verbs;
3127 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003128 }
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003129 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003130 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
3131
3132 codec->patch_ops = ad198x_patch_ops;
3133 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003134 case AD1988_AUTO:
3135 codec->patch_ops.init = ad1988_auto_init;
3136 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003137 case AD1988_LAPTOP:
3138 case AD1988_LAPTOP_DIG:
3139 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3140 break;
3141 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003142#ifdef CONFIG_SND_HDA_POWER_SAVE
3143 spec->loopback.amplist = ad1988_loopbacks;
3144#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003145 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003146
3147 return 0;
3148}
3149
3150
3151/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003152 * AD1884 / AD1984
3153 *
3154 * port-B - front line/mic-in
3155 * port-E - aux in/out
3156 * port-F - aux in/out
3157 * port-C - rear line/mic-in
3158 * port-D - rear line/hp-out
3159 * port-A - front line/hp-out
3160 *
3161 * AD1984 = AD1884 + two digital mic-ins
3162 *
3163 * FIXME:
3164 * For simplicity, we share the single DAC for both HP and line-outs
3165 * right now. The inidividual playbacks could be easily implemented,
3166 * but no build-up framework is given, so far.
3167 */
3168
3169static hda_nid_t ad1884_dac_nids[1] = {
3170 0x04,
3171};
3172
3173static hda_nid_t ad1884_adc_nids[2] = {
3174 0x08, 0x09,
3175};
3176
3177static hda_nid_t ad1884_capsrc_nids[2] = {
3178 0x0c, 0x0d,
3179};
3180
3181#define AD1884_SPDIF_OUT 0x02
3182
3183static struct hda_input_mux ad1884_capture_source = {
3184 .num_items = 4,
3185 .items = {
3186 { "Front Mic", 0x0 },
3187 { "Mic", 0x1 },
3188 { "CD", 0x2 },
3189 { "Mix", 0x3 },
3190 },
3191};
3192
3193static struct snd_kcontrol_new ad1884_base_mixers[] = {
3194 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3195 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3196 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3197 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3198 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3199 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3200 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3201 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3202 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3203 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3204 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3205 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003206 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
3207 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3208 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3209 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3210 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3211 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3212 {
3213 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3214 /* The multiple "Capture Source" controls confuse alsamixer
3215 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003216 */
3217 /* .name = "Capture Source", */
3218 .name = "Input Source",
3219 .count = 2,
3220 .info = ad198x_mux_enum_info,
3221 .get = ad198x_mux_enum_get,
3222 .put = ad198x_mux_enum_put,
3223 },
3224 /* SPDIF controls */
3225 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3226 {
3227 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3228 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3229 /* identical with ad1983 */
3230 .info = ad1983_spdif_route_info,
3231 .get = ad1983_spdif_route_get,
3232 .put = ad1983_spdif_route_put,
3233 },
3234 { } /* end */
3235};
3236
3237static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3238 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3239 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3240 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003241 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003242 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003243 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003244 { } /* end */
3245};
3246
3247/*
3248 * initialization verbs
3249 */
3250static struct hda_verb ad1884_init_verbs[] = {
3251 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003252 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3253 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003254 /* Port-A (HP) mixer */
3255 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3256 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3257 /* Port-A pin */
3258 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3259 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3260 /* HP selector - select DAC2 */
3261 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3262 /* Port-D (Line-out) mixer */
3263 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3264 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3265 /* Port-D pin */
3266 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3267 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3268 /* Mono-out mixer */
3269 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3270 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3271 /* Mono-out pin */
3272 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3273 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3274 /* Mono selector */
3275 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3276 /* Port-B (front mic) pin */
3277 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003278 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003279 /* Port-C (rear mic) pin */
3280 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003281 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003282 /* Analog mixer; mute as default */
3283 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3284 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3285 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3286 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3287 /* Analog Mix output amp */
3288 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3289 /* SPDIF output selector */
3290 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3291 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3292 { } /* end */
3293};
3294
Takashi Iwaicb53c622007-08-10 17:21:45 +02003295#ifdef CONFIG_SND_HDA_POWER_SAVE
3296static struct hda_amp_list ad1884_loopbacks[] = {
3297 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3298 { 0x20, HDA_INPUT, 1 }, /* Mic */
3299 { 0x20, HDA_INPUT, 2 }, /* CD */
3300 { 0x20, HDA_INPUT, 4 }, /* Docking */
3301 { } /* end */
3302};
3303#endif
3304
Takashi Iwai2134ea42008-01-10 16:53:55 +01003305static const char *ad1884_slave_vols[] = {
3306 "PCM Playback Volume",
3307 "Mic Playback Volume",
3308 "Mono Playback Volume",
3309 "Front Mic Playback Volume",
3310 "Mic Playback Volume",
3311 "CD Playback Volume",
3312 "Internal Mic Playback Volume",
Akinobu Mitabca68462009-04-06 18:42:42 +09003313 "Docking Mic Playback Volume",
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003314 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003315 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003316 NULL
3317};
3318
Takashi Iwai2bac6472007-05-18 18:21:41 +02003319static int patch_ad1884(struct hda_codec *codec)
3320{
3321 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003322 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003323
3324 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3325 if (spec == NULL)
3326 return -ENOMEM;
3327
Takashi Iwai2bac6472007-05-18 18:21:41 +02003328 codec->spec = spec;
3329
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003330 err = snd_hda_attach_beep_device(codec, 0x10);
3331 if (err < 0) {
3332 ad198x_free(codec);
3333 return err;
3334 }
3335 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3336
Takashi Iwai2bac6472007-05-18 18:21:41 +02003337 spec->multiout.max_channels = 2;
3338 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3339 spec->multiout.dac_nids = ad1884_dac_nids;
3340 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3341 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3342 spec->adc_nids = ad1884_adc_nids;
3343 spec->capsrc_nids = ad1884_capsrc_nids;
3344 spec->input_mux = &ad1884_capture_source;
3345 spec->num_mixers = 1;
3346 spec->mixers[0] = ad1884_base_mixers;
3347 spec->num_init_verbs = 1;
3348 spec->init_verbs[0] = ad1884_init_verbs;
3349 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003350#ifdef CONFIG_SND_HDA_POWER_SAVE
3351 spec->loopback.amplist = ad1884_loopbacks;
3352#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003353 spec->vmaster_nid = 0x04;
3354 /* we need to cover all playback volumes */
3355 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003356
3357 codec->patch_ops = ad198x_patch_ops;
3358
3359 return 0;
3360}
3361
3362/*
3363 * Lenovo Thinkpad T61/X61
3364 */
3365static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003366 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003367 .items = {
3368 { "Mic", 0x0 },
3369 { "Internal Mic", 0x1 },
3370 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003371 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003372 },
3373};
3374
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003375
3376/*
3377 * Dell Precision T3400
3378 */
3379static struct hda_input_mux ad1984_dell_desktop_capture_source = {
3380 .num_items = 3,
3381 .items = {
3382 { "Front Mic", 0x0 },
3383 { "Line-In", 0x1 },
3384 { "Mix", 0x3 },
3385 },
3386};
3387
3388
Takashi Iwai2bac6472007-05-18 18:21:41 +02003389static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3390 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3391 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3392 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3393 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3394 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3395 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3396 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3397 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3398 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3399 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003400 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3401 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003402 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003403 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3404 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3405 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3406 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3407 {
3408 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3409 /* The multiple "Capture Source" controls confuse alsamixer
3410 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003411 */
3412 /* .name = "Capture Source", */
3413 .name = "Input Source",
3414 .count = 2,
3415 .info = ad198x_mux_enum_info,
3416 .get = ad198x_mux_enum_get,
3417 .put = ad198x_mux_enum_put,
3418 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003419 /* SPDIF controls */
3420 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3421 {
3422 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3423 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3424 /* identical with ad1983 */
3425 .info = ad1983_spdif_route_info,
3426 .get = ad1983_spdif_route_get,
3427 .put = ad1983_spdif_route_put,
3428 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003429 { } /* end */
3430};
3431
3432/* additional verbs */
3433static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3434 /* Port-E (docking station mic) pin */
3435 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3436 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3437 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003438 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003439 /* Analog mixer - docking mic; mute as default */
3440 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003441 /* enable EAPD bit */
3442 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003443 { } /* end */
3444};
3445
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003446/*
3447 * Dell Precision T3400
3448 */
3449static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
3450 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3451 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3452 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3453 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3454 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3455 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3456 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3457 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3458 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003459 HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
3460 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3461 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3462 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3463 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3464 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3465 {
3466 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3467 /* The multiple "Capture Source" controls confuse alsamixer
3468 * So call somewhat different..
3469 */
3470 /* .name = "Capture Source", */
3471 .name = "Input Source",
3472 .count = 2,
3473 .info = ad198x_mux_enum_info,
3474 .get = ad198x_mux_enum_get,
3475 .put = ad198x_mux_enum_put,
3476 },
3477 { } /* end */
3478};
3479
Takashi Iwai2bac6472007-05-18 18:21:41 +02003480/* Digial MIC ADC NID 0x05 + 0x06 */
3481static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3482 struct hda_codec *codec,
3483 unsigned int stream_tag,
3484 unsigned int format,
3485 struct snd_pcm_substream *substream)
3486{
3487 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3488 stream_tag, 0, format);
3489 return 0;
3490}
3491
3492static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3493 struct hda_codec *codec,
3494 struct snd_pcm_substream *substream)
3495{
Takashi Iwai888afa12008-03-18 09:57:50 +01003496 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003497 return 0;
3498}
3499
3500static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3501 .substreams = 2,
3502 .channels_min = 2,
3503 .channels_max = 2,
3504 .nid = 0x05,
3505 .ops = {
3506 .prepare = ad1984_pcm_dmic_prepare,
3507 .cleanup = ad1984_pcm_dmic_cleanup
3508 },
3509};
3510
3511static int ad1984_build_pcms(struct hda_codec *codec)
3512{
3513 struct ad198x_spec *spec = codec->spec;
3514 struct hda_pcm *info;
3515 int err;
3516
3517 err = ad198x_build_pcms(codec);
3518 if (err < 0)
3519 return err;
3520
3521 info = spec->pcm_rec + codec->num_pcms;
3522 codec->num_pcms++;
3523 info->name = "AD1984 Digital Mic";
3524 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3525 return 0;
3526}
3527
3528/* models */
3529enum {
3530 AD1984_BASIC,
3531 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003532 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003533 AD1984_MODELS
3534};
3535
3536static const char *ad1984_models[AD1984_MODELS] = {
3537 [AD1984_BASIC] = "basic",
3538 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003539 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003540};
3541
3542static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3543 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003544 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003545 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003546 {}
3547};
3548
3549static int patch_ad1984(struct hda_codec *codec)
3550{
3551 struct ad198x_spec *spec;
3552 int board_config, err;
3553
3554 err = patch_ad1884(codec);
3555 if (err < 0)
3556 return err;
3557 spec = codec->spec;
3558 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3559 ad1984_models, ad1984_cfg_tbl);
3560 switch (board_config) {
3561 case AD1984_BASIC:
3562 /* additional digital mics */
3563 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3564 codec->patch_ops.build_pcms = ad1984_build_pcms;
3565 break;
3566 case AD1984_THINKPAD:
Jerone Youngebf00c52008-01-07 12:22:18 +01003567 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003568 spec->input_mux = &ad1984_thinkpad_capture_source;
3569 spec->mixers[0] = ad1984_thinkpad_mixers;
3570 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
3571 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003572 case AD1984_DELL_DESKTOP:
3573 spec->multiout.dig_out_nid = 0;
3574 spec->input_mux = &ad1984_dell_desktop_capture_source;
3575 spec->mixers[0] = ad1984_dell_desktop_mixers;
3576 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003577 }
3578 return 0;
3579}
3580
3581
3582/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003583 * AD1883 / AD1884A / AD1984A / AD1984B
3584 *
3585 * port-B (0x14) - front mic-in
3586 * port-E (0x1c) - rear mic-in
3587 * port-F (0x16) - CD / ext out
3588 * port-C (0x15) - rear line-in
3589 * port-D (0x12) - rear line-out
3590 * port-A (0x11) - front hp-out
3591 *
3592 * AD1984A = AD1884A + digital-mic
3593 * AD1883 = equivalent with AD1984A
3594 * AD1984B = AD1984A + extra SPDIF-out
3595 *
3596 * FIXME:
3597 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3598 */
3599
3600static hda_nid_t ad1884a_dac_nids[1] = {
3601 0x03,
3602};
3603
3604#define ad1884a_adc_nids ad1884_adc_nids
3605#define ad1884a_capsrc_nids ad1884_capsrc_nids
3606
3607#define AD1884A_SPDIF_OUT 0x02
3608
3609static struct hda_input_mux ad1884a_capture_source = {
3610 .num_items = 5,
3611 .items = {
3612 { "Front Mic", 0x0 },
3613 { "Mic", 0x4 },
3614 { "Line", 0x1 },
3615 { "CD", 0x2 },
3616 { "Mix", 0x3 },
3617 },
3618};
3619
3620static struct snd_kcontrol_new ad1884a_base_mixers[] = {
3621 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3622 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3623 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3624 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3625 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3626 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3627 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3628 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3629 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3630 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3631 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3632 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3633 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3634 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3635 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3636 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003637 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3638 HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
3639 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3640 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3641 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3642 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3643 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3644 {
3645 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3646 /* The multiple "Capture Source" controls confuse alsamixer
3647 * So call somewhat different..
3648 */
3649 /* .name = "Capture Source", */
3650 .name = "Input Source",
3651 .count = 2,
3652 .info = ad198x_mux_enum_info,
3653 .get = ad198x_mux_enum_get,
3654 .put = ad198x_mux_enum_put,
3655 },
3656 /* SPDIF controls */
3657 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3658 {
3659 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3660 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3661 /* identical with ad1983 */
3662 .info = ad1983_spdif_route_info,
3663 .get = ad1983_spdif_route_get,
3664 .put = ad1983_spdif_route_put,
3665 },
3666 { } /* end */
3667};
3668
3669/*
3670 * initialization verbs
3671 */
3672static struct hda_verb ad1884a_init_verbs[] = {
3673 /* DACs; unmute as default */
3674 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3675 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3676 /* Port-A (HP) mixer - route only from analog mixer */
3677 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3678 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3679 /* Port-A pin */
3680 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3681 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3682 /* Port-D (Line-out) mixer - route only from analog mixer */
3683 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3684 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3685 /* Port-D pin */
3686 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3687 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3688 /* Mono-out mixer - route only from analog mixer */
3689 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3690 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3691 /* Mono-out pin */
3692 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3693 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3694 /* Port-B (front mic) pin */
3695 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003696 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003697 /* Port-C (rear line-in) pin */
3698 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003699 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003700 /* Port-E (rear mic) pin */
3701 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3702 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3703 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
3704 /* Port-F (CD) pin */
3705 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3706 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3707 /* Analog mixer; mute as default */
3708 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3709 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3710 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3711 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3712 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
3713 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3714 /* Analog Mix output amp */
3715 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3716 /* capture sources */
3717 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
3718 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3719 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3720 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3721 /* SPDIF output amp */
3722 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3723 { } /* end */
3724};
3725
3726#ifdef CONFIG_SND_HDA_POWER_SAVE
3727static struct hda_amp_list ad1884a_loopbacks[] = {
3728 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3729 { 0x20, HDA_INPUT, 1 }, /* Mic */
3730 { 0x20, HDA_INPUT, 2 }, /* CD */
3731 { 0x20, HDA_INPUT, 4 }, /* Docking */
3732 { } /* end */
3733};
3734#endif
3735
3736/*
3737 * Laptop model
3738 *
3739 * Port A: Headphone jack
3740 * Port B: MIC jack
3741 * Port C: Internal MIC
3742 * Port D: Dock Line Out (if enabled)
3743 * Port E: Dock Line In (if enabled)
3744 * Port F: Internal speakers
3745 */
3746
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003747static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
3748 struct snd_ctl_elem_value *ucontrol)
3749{
3750 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3751 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
3752 int mute = (!ucontrol->value.integer.value[0] &&
3753 !ucontrol->value.integer.value[1]);
3754 /* toggle GPIO1 according to the mute state */
3755 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
3756 mute ? 0x02 : 0x0);
3757 return ret;
3758}
Takashi Iwaic5059252008-02-16 09:43:56 +01003759
3760static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
3761 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003762 {
3763 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3764 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003765 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003766 .info = snd_hda_mixer_amp_switch_info,
3767 .get = snd_hda_mixer_amp_switch_get,
3768 .put = ad1884a_mobile_master_sw_put,
3769 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3770 },
Takashi Iwaic5059252008-02-16 09:43:56 +01003771 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3772 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3773 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3774 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3775 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3776 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3777 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3778 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3779 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003780 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3781 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
3782 HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3783 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3784 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003785 { } /* end */
3786};
3787
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003788static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
3789 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db17e2009-07-02 16:10:23 +02003790 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
3791 {
3792 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3793 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003794 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai099db17e2009-07-02 16:10:23 +02003795 .info = snd_hda_mixer_amp_switch_info,
3796 .get = snd_hda_mixer_amp_switch_get,
3797 .put = ad1884a_mobile_master_sw_put,
3798 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3799 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003800 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3801 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02003802 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
3803 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003804 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3805 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003806 { } /* end */
3807};
3808
Takashi Iwaic5059252008-02-16 09:43:56 +01003809/* mute internal speaker if HP is plugged */
3810static void ad1884a_hp_automute(struct hda_codec *codec)
3811{
3812 unsigned int present;
3813
Takashi Iwaid56757a2009-11-18 08:00:14 +01003814 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01003815 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3816 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3817 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3818 present ? 0x00 : 0x02);
3819}
3820
Takashi Iwai269ef192008-05-30 15:32:15 +02003821/* switch to external mic if plugged */
3822static void ad1884a_hp_automic(struct hda_codec *codec)
3823{
3824 unsigned int present;
3825
Takashi Iwaid56757a2009-11-18 08:00:14 +01003826 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02003827 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
3828 present ? 0 : 1);
3829}
3830
Takashi Iwaic5059252008-02-16 09:43:56 +01003831#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02003832#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01003833
3834/* unsolicited event for HP jack sensing */
3835static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
3836{
Takashi Iwai269ef192008-05-30 15:32:15 +02003837 switch (res >> 26) {
3838 case AD1884A_HP_EVENT:
3839 ad1884a_hp_automute(codec);
3840 break;
3841 case AD1884A_MIC_EVENT:
3842 ad1884a_hp_automic(codec);
3843 break;
3844 }
Takashi Iwaic5059252008-02-16 09:43:56 +01003845}
3846
3847/* initialize jack-sensing, too */
3848static int ad1884a_hp_init(struct hda_codec *codec)
3849{
3850 ad198x_init(codec);
3851 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02003852 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01003853 return 0;
3854}
3855
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003856/* mute internal speaker if HP or docking HP is plugged */
3857static void ad1884a_laptop_automute(struct hda_codec *codec)
3858{
3859 unsigned int present;
3860
Takashi Iwaid56757a2009-11-18 08:00:14 +01003861 present = snd_hda_jack_detect(codec, 0x11);
3862 if (!present)
3863 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003864 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3865 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3866 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3867 present ? 0x00 : 0x02);
3868}
3869
3870/* switch to external mic if plugged */
3871static void ad1884a_laptop_automic(struct hda_codec *codec)
3872{
3873 unsigned int idx;
3874
Takashi Iwaid56757a2009-11-18 08:00:14 +01003875 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003876 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01003877 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003878 idx = 4;
3879 else
3880 idx = 1;
3881 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
3882}
3883
3884/* unsolicited event for HP jack sensing */
3885static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
3886 unsigned int res)
3887{
3888 switch (res >> 26) {
3889 case AD1884A_HP_EVENT:
3890 ad1884a_laptop_automute(codec);
3891 break;
3892 case AD1884A_MIC_EVENT:
3893 ad1884a_laptop_automic(codec);
3894 break;
3895 }
3896}
3897
3898/* initialize jack-sensing, too */
3899static int ad1884a_laptop_init(struct hda_codec *codec)
3900{
3901 ad198x_init(codec);
3902 ad1884a_laptop_automute(codec);
3903 ad1884a_laptop_automic(codec);
3904 return 0;
3905}
3906
Takashi Iwaic5059252008-02-16 09:43:56 +01003907/* additional verbs for laptop model */
3908static struct hda_verb ad1884a_laptop_verbs[] = {
3909 /* Port-A (HP) pin - always unmuted */
3910 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3911 /* Port-F (int speaker) mixer - route only from analog mixer */
3912 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3913 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08003914 /* Port-F (int speaker) pin */
3915 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01003916 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08003917 /* required for compaq 6530s/6531s speaker output */
3918 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003919 /* Port-C pin - internal mic-in */
3920 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3921 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3922 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02003923 /* Port-D (docking line-out) pin - default unmuted */
3924 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01003925 /* analog mix */
3926 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3927 /* unsolicited event for pin-sense */
3928 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003929 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003930 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003931 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02003932 /* allow to touch GPIO1 (for mute control) */
3933 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
3934 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
3935 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01003936 { } /* end */
3937};
3938
Takashi Iwai73156132009-04-23 08:24:48 +02003939static struct hda_verb ad1884a_mobile_verbs[] = {
3940 /* DACs; unmute as default */
3941 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3942 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3943 /* Port-A (HP) mixer - route only from analog mixer */
3944 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3945 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3946 /* Port-A pin */
3947 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3948 /* Port-A (HP) pin - always unmuted */
3949 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3950 /* Port-B (mic jack) pin */
3951 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3952 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3953 /* Port-C (int mic) pin */
3954 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3955 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3956 /* Port-F (int speaker) mixer - route only from analog mixer */
3957 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3958 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3959 /* Port-F pin */
3960 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3961 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3962 /* Analog mixer; mute as default */
3963 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3964 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3965 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3966 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3967 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3968 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3969 /* Analog Mix output amp */
3970 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3971 /* capture sources */
3972 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
3973 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3974 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3975 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3976 /* unsolicited event for pin-sense */
3977 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
3978 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db17e2009-07-02 16:10:23 +02003979 /* allow to touch GPIO1 (for mute control) */
3980 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
3981 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
3982 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02003983 { } /* end */
3984};
3985
Takashi Iwaic5059252008-02-16 09:43:56 +01003986/*
Takashi Iwaif0813742008-03-18 12:13:03 +01003987 * Thinkpad X300
3988 * 0x11 - HP
3989 * 0x12 - speaker
3990 * 0x14 - mic-in
3991 * 0x17 - built-in mic
3992 */
3993
3994static struct hda_verb ad1984a_thinkpad_verbs[] = {
3995 /* HP unmute */
3996 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3997 /* analog mix */
3998 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3999 /* turn on EAPD */
4000 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4001 /* unsolicited event for pin-sense */
4002 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4003 /* internal mic - dmic */
4004 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02004005 /* set magic COEFs for dmic */
4006 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4007 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01004008 { } /* end */
4009};
4010
4011static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
4012 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4013 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4014 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4015 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4016 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4017 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01004018 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
4019 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4020 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4021 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4022 {
4023 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4024 .name = "Capture Source",
4025 .info = ad198x_mux_enum_info,
4026 .get = ad198x_mux_enum_get,
4027 .put = ad198x_mux_enum_put,
4028 },
4029 { } /* end */
4030};
4031
4032static struct hda_input_mux ad1984a_thinkpad_capture_source = {
4033 .num_items = 3,
4034 .items = {
4035 { "Mic", 0x0 },
4036 { "Internal Mic", 0x5 },
4037 { "Mix", 0x3 },
4038 },
4039};
4040
4041/* mute internal speaker if HP is plugged */
4042static void ad1984a_thinkpad_automute(struct hda_codec *codec)
4043{
4044 unsigned int present;
4045
Takashi Iwaid56757a2009-11-18 08:00:14 +01004046 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01004047 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
4048 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4049}
4050
4051/* unsolicited event for HP jack sensing */
4052static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
4053 unsigned int res)
4054{
4055 if ((res >> 26) != AD1884A_HP_EVENT)
4056 return;
4057 ad1984a_thinkpad_automute(codec);
4058}
4059
4060/* initialize jack-sensing, too */
4061static int ad1984a_thinkpad_init(struct hda_codec *codec)
4062{
4063 ad198x_init(codec);
4064 ad1984a_thinkpad_automute(codec);
4065 return 0;
4066}
4067
4068/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004069 * HP Touchsmart
4070 * port-A (0x11) - front hp-out
4071 * port-B (0x14) - unused
4072 * port-C (0x15) - unused
4073 * port-D (0x12) - rear line out
4074 * port-E (0x1c) - front mic-in
4075 * port-F (0x16) - Internal speakers
4076 * digital-mic (0x17) - Internal mic
4077 */
4078
4079static struct hda_verb ad1984a_touchsmart_verbs[] = {
4080 /* DACs; unmute as default */
4081 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4082 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4083 /* Port-A (HP) mixer - route only from analog mixer */
4084 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4085 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4086 /* Port-A pin */
4087 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4088 /* Port-A (HP) pin - always unmuted */
4089 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4090 /* Port-E (int speaker) mixer - route only from analog mixer */
4091 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4092 /* Port-E pin */
4093 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4094 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4095 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4096 /* Port-F (int speaker) mixer - route only from analog mixer */
4097 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4098 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4099 /* Port-F pin */
4100 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4101 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4102 /* Analog mixer; mute as default */
4103 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4104 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4105 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4106 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4107 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4108 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4109 /* Analog Mix output amp */
4110 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4111 /* capture sources */
4112 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4113 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4114 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4115 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4116 /* unsolicited event for pin-sense */
4117 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4118 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4119 /* allow to touch GPIO1 (for mute control) */
4120 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4121 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4122 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4123 /* internal mic - dmic */
4124 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4125 /* set magic COEFs for dmic */
4126 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4127 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4128 { } /* end */
4129};
4130
4131static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
4132 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4133/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4134 {
4135 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004136 .subdevice = HDA_SUBDEV_AMP_FLAG,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004137 .name = "Master Playback Switch",
4138 .info = snd_hda_mixer_amp_switch_info,
4139 .get = snd_hda_mixer_amp_switch_get,
4140 .put = ad1884a_mobile_master_sw_put,
4141 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4142 },
4143 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4144 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4145 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4146 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4147 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
4148 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
4149 { } /* end */
4150};
4151
4152/* switch to external mic if plugged */
4153static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4154{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004155 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004156 snd_hda_codec_write(codec, 0x0c, 0,
4157 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004158 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004159 snd_hda_codec_write(codec, 0x0c, 0,
4160 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004161}
4162
4163
4164/* unsolicited event for HP jack sensing */
4165static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4166 unsigned int res)
4167{
4168 switch (res >> 26) {
4169 case AD1884A_HP_EVENT:
4170 ad1884a_hp_automute(codec);
4171 break;
4172 case AD1884A_MIC_EVENT:
4173 ad1984a_touchsmart_automic(codec);
4174 break;
4175 }
4176}
4177
4178/* initialize jack-sensing, too */
4179static int ad1984a_touchsmart_init(struct hda_codec *codec)
4180{
4181 ad198x_init(codec);
4182 ad1884a_hp_automute(codec);
4183 ad1984a_touchsmart_automic(codec);
4184 return 0;
4185}
4186
4187
4188/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004189 */
4190
4191enum {
4192 AD1884A_DESKTOP,
4193 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004194 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004195 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004196 AD1984A_TOUCHSMART,
Takashi Iwaic5059252008-02-16 09:43:56 +01004197 AD1884A_MODELS
4198};
4199
4200static const char *ad1884a_models[AD1884A_MODELS] = {
4201 [AD1884A_DESKTOP] = "desktop",
4202 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004203 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004204 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004205 [AD1984A_TOUCHSMART] = "touchsmart",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004206};
4207
4208static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
4209 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004210 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004211 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004212 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004213 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004214 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4215 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004216 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004217 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004218 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004219 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004220};
4221
4222static int patch_ad1884a(struct hda_codec *codec)
4223{
4224 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004225 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004226
4227 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4228 if (spec == NULL)
4229 return -ENOMEM;
4230
Takashi Iwaic5059252008-02-16 09:43:56 +01004231 codec->spec = spec;
4232
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004233 err = snd_hda_attach_beep_device(codec, 0x10);
4234 if (err < 0) {
4235 ad198x_free(codec);
4236 return err;
4237 }
4238 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4239
Takashi Iwaic5059252008-02-16 09:43:56 +01004240 spec->multiout.max_channels = 2;
4241 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4242 spec->multiout.dac_nids = ad1884a_dac_nids;
4243 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4244 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4245 spec->adc_nids = ad1884a_adc_nids;
4246 spec->capsrc_nids = ad1884a_capsrc_nids;
4247 spec->input_mux = &ad1884a_capture_source;
4248 spec->num_mixers = 1;
4249 spec->mixers[0] = ad1884a_base_mixers;
4250 spec->num_init_verbs = 1;
4251 spec->init_verbs[0] = ad1884a_init_verbs;
4252 spec->spdif_route = 0;
4253#ifdef CONFIG_SND_HDA_POWER_SAVE
4254 spec->loopback.amplist = ad1884a_loopbacks;
4255#endif
4256 codec->patch_ops = ad198x_patch_ops;
4257
4258 /* override some parameters */
4259 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004260 ad1884a_models,
4261 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01004262 switch (board_config) {
4263 case AD1884A_LAPTOP:
4264 spec->mixers[0] = ad1884a_laptop_mixers;
4265 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4266 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004267 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4268 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004269 /* set the upper-limit for mixer amp to 0dB for avoiding the
4270 * possible damage by overloading
4271 */
4272 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4273 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4274 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4275 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4276 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004277 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004278 case AD1884A_MOBILE:
4279 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004280 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004281 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004282 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4283 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004284 /* set the upper-limit for mixer amp to 0dB for avoiding the
4285 * possible damage by overloading
4286 */
4287 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4288 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4289 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4290 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4291 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004292 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004293 case AD1884A_THINKPAD:
4294 spec->mixers[0] = ad1984a_thinkpad_mixers;
4295 spec->init_verbs[spec->num_init_verbs++] =
4296 ad1984a_thinkpad_verbs;
4297 spec->multiout.dig_out_nid = 0;
4298 spec->input_mux = &ad1984a_thinkpad_capture_source;
4299 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4300 codec->patch_ops.init = ad1984a_thinkpad_init;
4301 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004302 case AD1984A_TOUCHSMART:
4303 spec->mixers[0] = ad1984a_touchsmart_mixers;
4304 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4305 spec->multiout.dig_out_nid = 0;
4306 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4307 codec->patch_ops.init = ad1984a_touchsmart_init;
4308 /* set the upper-limit for mixer amp to 0dB for avoiding the
4309 * possible damage by overloading
4310 */
4311 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4312 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4313 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4314 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4315 (1 << AC_AMPCAP_MUTE_SHIFT));
4316 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004317 }
4318
4319 return 0;
4320}
4321
4322
4323/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004324 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004325 *
4326 * port-A - front hp-out
4327 * port-B - front mic-in
4328 * port-C - rear line-in, shared surr-out (3stack)
4329 * port-D - rear line-out
4330 * port-E - rear mic-in, shared clfe-out (3stack)
4331 * port-F - rear surr-out (6stack)
4332 * port-G - rear clfe-out (6stack)
4333 */
4334
4335static hda_nid_t ad1882_dac_nids[3] = {
4336 0x04, 0x03, 0x05
4337};
4338
4339static hda_nid_t ad1882_adc_nids[2] = {
4340 0x08, 0x09,
4341};
4342
4343static hda_nid_t ad1882_capsrc_nids[2] = {
4344 0x0c, 0x0d,
4345};
4346
4347#define AD1882_SPDIF_OUT 0x02
4348
4349/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
4350static struct hda_input_mux ad1882_capture_source = {
4351 .num_items = 5,
4352 .items = {
4353 { "Front Mic", 0x1 },
4354 { "Mic", 0x4 },
4355 { "Line", 0x2 },
4356 { "CD", 0x3 },
4357 { "Mix", 0x7 },
4358 },
4359};
4360
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004361/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
4362static struct hda_input_mux ad1882a_capture_source = {
4363 .num_items = 5,
4364 .items = {
4365 { "Front Mic", 0x1 },
4366 { "Mic", 0x4},
4367 { "Line", 0x2 },
4368 { "Digital Mic", 0x06 },
4369 { "Mix", 0x7 },
4370 },
4371};
4372
Takashi Iwai0ac85512007-06-20 15:46:13 +02004373static struct snd_kcontrol_new ad1882_base_mixers[] = {
4374 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4375 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4376 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4377 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4378 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4379 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4380 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4381 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004382
Takashi Iwai0ac85512007-06-20 15:46:13 +02004383 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
4384 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
4385 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
4386 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4387 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4388 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4389 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4390 {
4391 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4392 /* The multiple "Capture Source" controls confuse alsamixer
4393 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004394 */
4395 /* .name = "Capture Source", */
4396 .name = "Input Source",
4397 .count = 2,
4398 .info = ad198x_mux_enum_info,
4399 .get = ad198x_mux_enum_get,
4400 .put = ad198x_mux_enum_put,
4401 },
4402 /* SPDIF controls */
4403 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4404 {
4405 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4406 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4407 /* identical with ad1983 */
4408 .info = ad1983_spdif_route_info,
4409 .get = ad1983_spdif_route_get,
4410 .put = ad1983_spdif_route_put,
4411 },
4412 { } /* end */
4413};
4414
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004415static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
4416 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4417 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4418 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4419 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4420 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4421 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4422 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4423 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004424 { } /* end */
4425};
4426
4427static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
4428 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4429 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4430 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4431 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4432 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4433 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4434 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4435 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004436 HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
4437 { } /* end */
4438};
4439
Takashi Iwai0ac85512007-06-20 15:46:13 +02004440static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
4441 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4442 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4443 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4444 {
4445 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4446 .name = "Channel Mode",
4447 .info = ad198x_ch_mode_info,
4448 .get = ad198x_ch_mode_get,
4449 .put = ad198x_ch_mode_put,
4450 },
4451 { } /* end */
4452};
4453
4454static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
4455 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4456 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4457 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4458 { } /* end */
4459};
4460
4461static struct hda_verb ad1882_ch2_init[] = {
4462 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4463 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4464 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4465 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4466 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4467 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4468 { } /* end */
4469};
4470
4471static struct hda_verb ad1882_ch4_init[] = {
4472 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4473 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4474 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4475 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4476 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4477 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4478 { } /* end */
4479};
4480
4481static struct hda_verb ad1882_ch6_init[] = {
4482 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4483 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4484 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4485 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4486 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4487 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4488 { } /* end */
4489};
4490
4491static struct hda_channel_mode ad1882_modes[3] = {
4492 { 2, ad1882_ch2_init },
4493 { 4, ad1882_ch4_init },
4494 { 6, ad1882_ch6_init },
4495};
4496
4497/*
4498 * initialization verbs
4499 */
4500static struct hda_verb ad1882_init_verbs[] = {
4501 /* DACs; mute as default */
4502 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4503 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4504 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4505 /* Port-A (HP) mixer */
4506 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4507 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4508 /* Port-A pin */
4509 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4510 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4511 /* HP selector - select DAC2 */
4512 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4513 /* Port-D (Line-out) mixer */
4514 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4515 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4516 /* Port-D pin */
4517 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4518 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4519 /* Mono-out mixer */
4520 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4521 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4522 /* Mono-out pin */
4523 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4524 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4525 /* Port-B (front mic) pin */
4526 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4527 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4528 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4529 /* Port-C (line-in) pin */
4530 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4531 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4532 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4533 /* Port-C mixer - mute as input */
4534 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4535 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4536 /* Port-E (mic-in) pin */
4537 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4538 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4539 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4540 /* Port-E mixer - mute as input */
4541 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4542 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4543 /* Port-F (surround) */
4544 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4545 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4546 /* Port-G (CLFE) */
4547 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4548 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4549 /* Analog mixer; mute as default */
4550 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4551 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4552 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4553 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4554 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4555 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4556 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4557 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4558 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4559 /* Analog Mix output amp */
4560 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4561 /* SPDIF output selector */
4562 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4563 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4564 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4565 { } /* end */
4566};
4567
Takashi Iwaicb53c622007-08-10 17:21:45 +02004568#ifdef CONFIG_SND_HDA_POWER_SAVE
4569static struct hda_amp_list ad1882_loopbacks[] = {
4570 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4571 { 0x20, HDA_INPUT, 1 }, /* Mic */
4572 { 0x20, HDA_INPUT, 4 }, /* Line */
4573 { 0x20, HDA_INPUT, 6 }, /* CD */
4574 { } /* end */
4575};
4576#endif
4577
Takashi Iwai0ac85512007-06-20 15:46:13 +02004578/* models */
4579enum {
4580 AD1882_3STACK,
4581 AD1882_6STACK,
4582 AD1882_MODELS
4583};
4584
4585static const char *ad1882_models[AD1986A_MODELS] = {
4586 [AD1882_3STACK] = "3stack",
4587 [AD1882_6STACK] = "6stack",
4588};
4589
4590
4591static int patch_ad1882(struct hda_codec *codec)
4592{
4593 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004594 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004595
4596 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4597 if (spec == NULL)
4598 return -ENOMEM;
4599
Takashi Iwai0ac85512007-06-20 15:46:13 +02004600 codec->spec = spec;
4601
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004602 err = snd_hda_attach_beep_device(codec, 0x10);
4603 if (err < 0) {
4604 ad198x_free(codec);
4605 return err;
4606 }
4607 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4608
Takashi Iwai0ac85512007-06-20 15:46:13 +02004609 spec->multiout.max_channels = 6;
4610 spec->multiout.num_dacs = 3;
4611 spec->multiout.dac_nids = ad1882_dac_nids;
4612 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
4613 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
4614 spec->adc_nids = ad1882_adc_nids;
4615 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004616 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004617 spec->input_mux = &ad1882_capture_source;
4618 else
4619 spec->input_mux = &ad1882a_capture_source;
4620 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004621 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004622 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004623 spec->mixers[1] = ad1882_loopback_mixers;
4624 else
4625 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004626 spec->num_init_verbs = 1;
4627 spec->init_verbs[0] = ad1882_init_verbs;
4628 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02004629#ifdef CONFIG_SND_HDA_POWER_SAVE
4630 spec->loopback.amplist = ad1882_loopbacks;
4631#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01004632 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004633
4634 codec->patch_ops = ad198x_patch_ops;
4635
4636 /* override some parameters */
4637 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
4638 ad1882_models, NULL);
4639 switch (board_config) {
4640 default:
4641 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004642 spec->num_mixers = 3;
4643 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004644 spec->channel_mode = ad1882_modes;
4645 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
4646 spec->need_dac_fix = 1;
4647 spec->multiout.max_channels = 2;
4648 spec->multiout.num_dacs = 1;
4649 break;
4650 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004651 spec->num_mixers = 3;
4652 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004653 break;
4654 }
4655 return 0;
4656}
4657
4658
4659/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004660 * patch entries
4661 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004662static struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004663 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02004664 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004665 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004666 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004667 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
4668 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02004669 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
4670 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004671 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004672 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01004673 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02004674 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004675 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02004676 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
4677 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004678 {} /* terminator */
4679};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004680
4681MODULE_ALIAS("snd-hda-codec-id:11d4*");
4682
4683MODULE_LICENSE("GPL");
4684MODULE_DESCRIPTION("Analog Devices HD-audio codec");
4685
4686static struct hda_codec_preset_list analog_list = {
4687 .preset = snd_hda_preset_analog,
4688 .owner = THIS_MODULE,
4689};
4690
4691static int __init patch_analog_init(void)
4692{
4693 return snd_hda_add_codec_preset(&analog_list);
4694}
4695
4696static void __exit patch_analog_exit(void)
4697{
4698 snd_hda_delete_codec_preset(&analog_list);
4699}
4700
4701module_init(patch_analog_init)
4702module_exit(patch_analog_exit)