blob: 6106dfe8ec04ebec28edc7be255cfde7259e2d3b [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;
75
Takashi Iwaicb53c622007-08-10 17:21:45 +020076#ifdef CONFIG_SND_HDA_POWER_SAVE
77 struct hda_loopback_check loopback;
78#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +010079 /* for virtual master */
80 hda_nid_t vmaster_nid;
Takashi Iwai2134ea42008-01-10 16:53:55 +010081 const char **slave_vols;
82 const char **slave_sws;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083};
84
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020085/*
86 * input MUX handling (common part)
87 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010088static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020089{
90 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
91 struct ad198x_spec *spec = codec->spec;
92
93 return snd_hda_input_mux_info(spec->input_mux, uinfo);
94}
95
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010096static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020097{
98 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
99 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100100 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200101
Takashi Iwai985be542005-11-02 18:26:49 +0100102 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200103 return 0;
104}
105
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100106static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200107{
108 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
109 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100110 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200111
112 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai2e5b9562005-11-21 16:36:15 +0100113 spec->capsrc_nids[adc_idx],
114 &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200115}
116
117/*
118 * initialization (common callbacks)
119 */
120static int ad198x_init(struct hda_codec *codec)
121{
122 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100123 int i;
124
125 for (i = 0; i < spec->num_init_verbs; i++)
126 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200127 return 0;
128}
129
Takashi Iwai2134ea42008-01-10 16:53:55 +0100130static const char *ad_slave_vols[] = {
131 "Front Playback Volume",
132 "Surround Playback Volume",
133 "Center Playback Volume",
134 "LFE Playback Volume",
135 "Side Playback Volume",
136 "Headphone Playback Volume",
137 "Mono Playback Volume",
Takashi Iwai628ed132008-01-25 11:56:57 +0100138 "Speaker Playback Volume",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100139 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100140 NULL
141};
142
143static const char *ad_slave_sws[] = {
144 "Front Playback Switch",
145 "Surround Playback Switch",
146 "Center Playback Switch",
147 "LFE Playback Switch",
148 "Side Playback Switch",
149 "Headphone Playback Switch",
150 "Mono Playback Switch",
Takashi Iwai628ed132008-01-25 11:56:57 +0100151 "Speaker Playback Switch",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100152 "IEC958 Playback Switch",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100153 NULL
154};
155
Takashi Iwai603c4012008-07-30 15:01:44 +0200156static void ad198x_free_kctls(struct hda_codec *codec);
157
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100158/* additional beep mixers; the actual parameters are overwritten at build */
159static struct snd_kcontrol_new ad_beep_mixer[] = {
160 HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
161 HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_OUTPUT),
162 { } /* end */
163};
164
165#define set_beep_amp(spec, nid, idx, dir) \
166 ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
167
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200168static int ad198x_build_controls(struct hda_codec *codec)
169{
170 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100171 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200172 int err;
173
Takashi Iwai985be542005-11-02 18:26:49 +0100174 for (i = 0; i < spec->num_mixers; i++) {
175 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
176 if (err < 0)
177 return err;
178 }
179 if (spec->multiout.dig_out_nid) {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200180 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100181 if (err < 0)
182 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100183 err = snd_hda_create_spdif_share_sw(codec,
184 &spec->multiout);
185 if (err < 0)
186 return err;
187 spec->multiout.share_spdif = 1;
Takashi Iwai985be542005-11-02 18:26:49 +0100188 }
189 if (spec->dig_in_nid) {
190 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
191 if (err < 0)
192 return err;
193 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100194
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100195 /* create beep controls if needed */
196 if (spec->beep_amp) {
197 struct snd_kcontrol_new *knew;
198 for (knew = ad_beep_mixer; knew->name; knew++) {
199 struct snd_kcontrol *kctl;
200 kctl = snd_ctl_new1(knew, codec);
201 if (!kctl)
202 return -ENOMEM;
203 kctl->private_value = spec->beep_amp;
204 err = snd_hda_ctl_add(codec, kctl);
205 if (err < 0)
206 return err;
207 }
208 }
209
Takashi Iwai2134ea42008-01-10 16:53:55 +0100210 /* if we have no master control, let's create it */
211 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100212 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100213 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100214 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100215 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100216 vmaster_tlv,
Takashi Iwai2134ea42008-01-10 16:53:55 +0100217 (spec->slave_vols ?
218 spec->slave_vols : ad_slave_vols));
219 if (err < 0)
220 return err;
221 }
222 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
223 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
224 NULL,
225 (spec->slave_sws ?
226 spec->slave_sws : ad_slave_sws));
227 if (err < 0)
228 return err;
229 }
230
Takashi Iwai603c4012008-07-30 15:01:44 +0200231 ad198x_free_kctls(codec); /* no longer needed */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200232 return 0;
233}
234
Takashi Iwaicb53c622007-08-10 17:21:45 +0200235#ifdef CONFIG_SND_HDA_POWER_SAVE
236static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
237{
238 struct ad198x_spec *spec = codec->spec;
239 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
240}
241#endif
242
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200243/*
244 * Analog playback callbacks
245 */
246static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
247 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100248 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200249{
250 struct ad198x_spec *spec = codec->spec;
Takashi Iwai9a081602008-02-12 18:37:26 +0100251 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
252 hinfo);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200253}
254
255static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
256 struct hda_codec *codec,
257 unsigned int stream_tag,
258 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100259 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200260{
261 struct ad198x_spec *spec = codec->spec;
262 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
263 format, substream);
264}
265
266static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
267 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100268 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200269{
270 struct ad198x_spec *spec = codec->spec;
271 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
272}
273
274/*
275 * Digital out
276 */
277static int ad198x_dig_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;
282 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
283}
284
285static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
286 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100287 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200288{
289 struct ad198x_spec *spec = codec->spec;
290 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
291}
292
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200293static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
294 struct hda_codec *codec,
295 unsigned int stream_tag,
296 unsigned int format,
297 struct snd_pcm_substream *substream)
298{
299 struct ad198x_spec *spec = codec->spec;
300 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
301 format, substream);
302}
303
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200304/*
305 * Analog capture
306 */
307static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
308 struct hda_codec *codec,
309 unsigned int stream_tag,
310 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100311 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200312{
313 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100314 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
315 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200316 return 0;
317}
318
319static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
320 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100321 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200322{
323 struct ad198x_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +0100324 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200325 return 0;
326}
327
328
329/*
330 */
331static struct hda_pcm_stream ad198x_pcm_analog_playback = {
332 .substreams = 1,
333 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100334 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200335 .nid = 0, /* fill later */
336 .ops = {
337 .open = ad198x_playback_pcm_open,
338 .prepare = ad198x_playback_pcm_prepare,
339 .cleanup = ad198x_playback_pcm_cleanup
340 },
341};
342
343static struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100344 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200345 .channels_min = 2,
346 .channels_max = 2,
347 .nid = 0, /* fill later */
348 .ops = {
349 .prepare = ad198x_capture_pcm_prepare,
350 .cleanup = ad198x_capture_pcm_cleanup
351 },
352};
353
354static struct hda_pcm_stream ad198x_pcm_digital_playback = {
355 .substreams = 1,
356 .channels_min = 2,
357 .channels_max = 2,
358 .nid = 0, /* fill later */
359 .ops = {
360 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200361 .close = ad198x_dig_playback_pcm_close,
362 .prepare = ad198x_dig_playback_pcm_prepare
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200363 },
364};
365
Takashi Iwai985be542005-11-02 18:26:49 +0100366static struct hda_pcm_stream ad198x_pcm_digital_capture = {
367 .substreams = 1,
368 .channels_min = 2,
369 .channels_max = 2,
370 /* NID is set in alc_build_pcms */
371};
372
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200373static int ad198x_build_pcms(struct hda_codec *codec)
374{
375 struct ad198x_spec *spec = codec->spec;
376 struct hda_pcm *info = spec->pcm_rec;
377
378 codec->num_pcms = 1;
379 codec->pcm_info = info;
380
381 info->name = "AD198x Analog";
382 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
383 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
384 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
385 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100386 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
387 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200388
389 if (spec->multiout.dig_out_nid) {
390 info++;
391 codec->num_pcms++;
392 info->name = "AD198x Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +0100393 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200394 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
395 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100396 if (spec->dig_in_nid) {
397 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
398 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
399 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200400 }
401
402 return 0;
403}
404
Takashi Iwai603c4012008-07-30 15:01:44 +0200405static void ad198x_free_kctls(struct hda_codec *codec)
406{
407 struct ad198x_spec *spec = codec->spec;
408
409 if (spec->kctls.list) {
410 struct snd_kcontrol_new *kctl = spec->kctls.list;
411 int i;
412 for (i = 0; i < spec->kctls.used; i++)
413 kfree(kctl[i].name);
414 }
415 snd_array_free(&spec->kctls);
416}
417
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200418static void ad198x_free(struct hda_codec *codec)
419{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100420 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +0100421
Takashi Iwai603c4012008-07-30 15:01:44 +0200422 if (!spec)
423 return;
424
425 ad198x_free_kctls(codec);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100426 kfree(spec);
427 snd_hda_detach_beep_device(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200428}
429
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200430static struct hda_codec_ops ad198x_patch_ops = {
431 .build_controls = ad198x_build_controls,
432 .build_pcms = ad198x_build_pcms,
433 .init = ad198x_init,
434 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200435#ifdef CONFIG_SND_HDA_POWER_SAVE
436 .check_power_status = ad198x_check_power_status,
437#endif
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200438};
439
440
441/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100442 * EAPD control
443 * the private value = nid | (invert << 8)
444 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200445#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100446
447static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
448 struct snd_ctl_elem_value *ucontrol)
449{
450 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
451 struct ad198x_spec *spec = codec->spec;
452 int invert = (kcontrol->private_value >> 8) & 1;
453 if (invert)
454 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
455 else
456 ucontrol->value.integer.value[0] = spec->cur_eapd;
457 return 0;
458}
459
460static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
461 struct snd_ctl_elem_value *ucontrol)
462{
463 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
464 struct ad198x_spec *spec = codec->spec;
465 int invert = (kcontrol->private_value >> 8) & 1;
466 hda_nid_t nid = kcontrol->private_value & 0xff;
467 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100468 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwai18a815d2006-03-01 19:54:39 +0100469 if (invert)
470 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200471 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100472 return 0;
473 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200474 snd_hda_codec_write_cache(codec, nid,
475 0, AC_VERB_SET_EAPD_BTLENABLE,
476 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100477 return 1;
478}
479
Takashi Iwai9230d212006-03-13 13:49:49 +0100480static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
481 struct snd_ctl_elem_info *uinfo);
482static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
483 struct snd_ctl_elem_value *ucontrol);
484static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
485 struct snd_ctl_elem_value *ucontrol);
486
487
Takashi Iwai18a815d2006-03-01 19:54:39 +0100488/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200489 * AD1986A specific
490 */
491
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492#define AD1986A_SPDIF_OUT 0x02
493#define AD1986A_FRONT_DAC 0x03
494#define AD1986A_SURR_DAC 0x04
495#define AD1986A_CLFE_DAC 0x05
496#define AD1986A_ADC 0x06
497
498static hda_nid_t ad1986a_dac_nids[3] = {
499 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
500};
Takashi Iwai985be542005-11-02 18:26:49 +0100501static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +0100502static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503
504static struct hda_input_mux ad1986a_capture_source = {
505 .num_items = 7,
506 .items = {
507 { "Mic", 0x0 },
508 { "CD", 0x1 },
509 { "Aux", 0x3 },
510 { "Line", 0x4 },
511 { "Mix", 0x5 },
512 { "Mono", 0x6 },
513 { "Phone", 0x7 },
514 },
515};
516
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517
Takashi Iwai532d5382007-07-27 19:02:40 +0200518static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
519 .ops = &snd_hda_bind_vol,
520 .values = {
521 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
522 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
523 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
524 0
525 },
526};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
Takashi Iwai532d5382007-07-27 19:02:40 +0200528static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
529 .ops = &snd_hda_bind_sw,
530 .values = {
531 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
532 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
533 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
534 0
535 },
536};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
538/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 * mixers
540 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100541static struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200542 /*
543 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
544 */
545 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
546 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
548 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
549 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
550 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
551 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
552 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
553 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
554 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
555 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
556 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
557 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
558 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
559 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
560 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
561 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
562 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
563 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
564 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100565 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
567 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
568 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
569 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
570 {
571 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
572 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200573 .info = ad198x_mux_enum_info,
574 .get = ad198x_mux_enum_get,
575 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 },
577 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
578 { } /* end */
579};
580
Takashi Iwai9230d212006-03-13 13:49:49 +0100581/* additional mixers for 3stack mode */
582static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
583 {
584 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
585 .name = "Channel Mode",
586 .info = ad198x_ch_mode_info,
587 .get = ad198x_ch_mode_get,
588 .put = ad198x_ch_mode_put,
589 },
590 { } /* end */
591};
592
593/* laptop model - 2ch only */
594static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
595
Takashi Iwai20a45e82007-08-15 22:20:45 +0200596/* master controls both pins 0x1a and 0x1b */
597static struct hda_bind_ctls ad1986a_laptop_master_vol = {
598 .ops = &snd_hda_bind_vol,
599 .values = {
600 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
601 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
602 0,
603 },
604};
605
606static struct hda_bind_ctls ad1986a_laptop_master_sw = {
607 .ops = &snd_hda_bind_sw,
608 .values = {
609 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
610 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
611 0,
612 },
613};
614
Takashi Iwai9230d212006-03-13 13:49:49 +0100615static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
616 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
617 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200618 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
619 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100620 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
621 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
622 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
623 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
624 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
625 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
626 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
627 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100628 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100629 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100630 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
631 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
632 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
633 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
634 {
635 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
636 .name = "Capture Source",
637 .info = ad198x_mux_enum_info,
638 .get = ad198x_mux_enum_get,
639 .put = ad198x_mux_enum_put,
640 },
641 { } /* end */
642};
643
Takashi Iwai825aa9722006-03-17 10:50:49 +0100644/* laptop-eapd model - 2ch only */
645
Takashi Iwai825aa9722006-03-17 10:50:49 +0100646static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
647 .num_items = 3,
648 .items = {
649 { "Mic", 0x0 },
650 { "Internal Mic", 0x4 },
651 { "Mix", 0x5 },
652 },
653};
654
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100655static struct hda_input_mux ad1986a_automic_capture_source = {
656 .num_items = 2,
657 .items = {
658 { "Mic", 0x0 },
659 { "Mix", 0x5 },
660 },
661};
662
Takashi Iwai825aa9722006-03-17 10:50:49 +0100663static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200664 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
665 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai825aa9722006-03-17 10:50:49 +0100666 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
667 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100668 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
669 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
670 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
671 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
672 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
673 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
674 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
675 {
676 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
677 .name = "Capture Source",
678 .info = ad198x_mux_enum_info,
679 .get = ad198x_mux_enum_get,
680 .put = ad198x_mux_enum_put,
681 },
682 {
683 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
684 .name = "External Amplifier",
685 .info = ad198x_eapd_info,
686 .get = ad198x_eapd_get,
687 .put = ad198x_eapd_put,
688 .private_value = 0x1b | (1 << 8), /* port-D, inversed */
689 },
690 { } /* end */
691};
692
693static struct snd_kcontrol_new ad1986a_samsung_mixers[] = {
694 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
695 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
696 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
697 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai825aa9722006-03-17 10:50:49 +0100698 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
699 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100700 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai825aa9722006-03-17 10:50:49 +0100701 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
702 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
703 {
704 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
705 .name = "Capture Source",
706 .info = ad198x_mux_enum_info,
707 .get = ad198x_mux_enum_get,
708 .put = ad198x_mux_enum_put,
709 },
710 {
711 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
712 .name = "External Amplifier",
713 .info = ad198x_eapd_info,
714 .get = ad198x_eapd_get,
715 .put = ad198x_eapd_put,
716 .private_value = 0x1b | (1 << 8), /* port-D, inversed */
717 },
718 { } /* end */
719};
720
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100721/* re-connect the mic boost input according to the jack sensing */
722static void ad1986a_automic(struct hda_codec *codec)
723{
724 unsigned int present;
725 present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0);
726 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
727 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
728 (present & AC_PINSENSE_PRESENCE) ? 0 : 2);
729}
730
731#define AD1986A_MIC_EVENT 0x36
732
733static void ad1986a_automic_unsol_event(struct hda_codec *codec,
734 unsigned int res)
735{
736 if ((res >> 26) != AD1986A_MIC_EVENT)
737 return;
738 ad1986a_automic(codec);
739}
740
741static int ad1986a_automic_init(struct hda_codec *codec)
742{
743 ad198x_init(codec);
744 ad1986a_automic(codec);
745 return 0;
746}
747
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200748/* laptop-automute - 2ch only */
749
750static void ad1986a_update_hp(struct hda_codec *codec)
751{
752 struct ad198x_spec *spec = codec->spec;
753 unsigned int mute;
754
755 if (spec->jack_present)
756 mute = HDA_AMP_MUTE; /* mute internal speaker */
757 else
758 /* unmute internal speaker if necessary */
759 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
760 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
761 HDA_AMP_MUTE, mute);
762}
763
764static void ad1986a_hp_automute(struct hda_codec *codec)
765{
766 struct ad198x_spec *spec = codec->spec;
767 unsigned int present;
768
769 present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0);
Takashi Iwai53eb1b82007-10-17 10:09:32 +0200770 /* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
771 spec->jack_present = !(present & 0x80000000);
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200772 ad1986a_update_hp(codec);
773}
774
775#define AD1986A_HP_EVENT 0x37
776
777static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
778{
779 if ((res >> 26) != AD1986A_HP_EVENT)
780 return;
781 ad1986a_hp_automute(codec);
782}
783
784static int ad1986a_hp_init(struct hda_codec *codec)
785{
786 ad198x_init(codec);
787 ad1986a_hp_automute(codec);
788 return 0;
789}
790
791/* bind hp and internal speaker mute (with plug check) */
792static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
793 struct snd_ctl_elem_value *ucontrol)
794{
795 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
796 long *valp = ucontrol->value.integer.value;
797 int change;
798
799 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
800 HDA_AMP_MUTE,
801 valp[0] ? 0 : HDA_AMP_MUTE);
802 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
803 HDA_AMP_MUTE,
804 valp[1] ? 0 : HDA_AMP_MUTE);
805 if (change)
806 ad1986a_update_hp(codec);
807 return change;
808}
809
810static struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = {
811 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
812 {
813 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
814 .name = "Master Playback Switch",
815 .info = snd_hda_mixer_amp_switch_info,
816 .get = snd_hda_mixer_amp_switch_get,
817 .put = ad1986a_hp_master_sw_put,
818 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
819 },
820 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
821 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
822 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT),
823 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT),
824 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
825 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
826 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200827 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
828 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
829 {
830 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
831 .name = "Capture Source",
832 .info = ad198x_mux_enum_info,
833 .get = ad198x_mux_enum_get,
834 .put = ad198x_mux_enum_put,
835 },
836 {
837 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
838 .name = "External Amplifier",
839 .info = ad198x_eapd_info,
840 .get = ad198x_eapd_get,
841 .put = ad198x_eapd_put,
842 .private_value = 0x1b | (1 << 8), /* port-D, inversed */
843 },
844 { } /* end */
845};
846
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847/*
848 * initialization verbs
849 */
850static struct hda_verb ad1986a_init_verbs[] = {
851 /* Front, Surround, CLFE DAC; mute as default */
852 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
853 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
854 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
855 /* Downmix - off */
856 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
857 /* HP, Line-Out, Surround, CLFE selectors */
858 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
859 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
860 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
861 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
862 /* Mono selector */
863 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
864 /* Mic selector: Mic 1/2 pin */
865 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
866 /* Line-in selector: Line-in */
867 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
868 /* Mic 1/2 swap */
869 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
870 /* Record selector: mic */
871 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
872 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
873 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
874 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
875 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
876 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
877 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
878 /* PC beep */
879 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
880 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
881 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
882 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
883 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
884 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
885 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200886 /* HP Pin */
887 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
888 /* Front, Surround, CLFE Pins */
889 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
890 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
891 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
892 /* Mono Pin */
893 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
894 /* Mic Pin */
895 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
896 /* Line, Aux, CD, Beep-In Pin */
897 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
898 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
899 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
900 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
901 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 { } /* end */
903};
904
Takashi Iwai9230d212006-03-13 13:49:49 +0100905static struct hda_verb ad1986a_ch2_init[] = {
906 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200907 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
908 /* Line-in selectors */
909 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100910 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200911 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
912 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
913 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100914 { } /* end */
915};
916
917static struct hda_verb ad1986a_ch4_init[] = {
918 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200919 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
920 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100921 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200922 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
923 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100924 { } /* end */
925};
926
927static struct hda_verb ad1986a_ch6_init[] = {
928 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200929 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
930 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100931 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200932 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
933 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100934 { } /* end */
935};
936
937static struct hda_channel_mode ad1986a_modes[3] = {
938 { 2, ad1986a_ch2_init },
939 { 4, ad1986a_ch4_init },
940 { 6, ad1986a_ch6_init },
941};
942
Takashi Iwai825aa9722006-03-17 10:50:49 +0100943/* eapd initialization */
944static struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +0100945 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa9722006-03-17 10:50:49 +0100946 {}
947};
948
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100949static struct hda_verb ad1986a_automic_verbs[] = {
950 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
951 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
952 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
953 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
954 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
955 {}
956};
957
Tobin Davisf36090f2007-01-08 11:07:12 +0100958/* Ultra initialization */
959static struct hda_verb ad1986a_ultra_init[] = {
960 /* eapd initialization */
961 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
962 /* CLFE -> Mic in */
963 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
964 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
965 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
966 { } /* end */
967};
968
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200969/* pin sensing on HP jack */
970static struct hda_verb ad1986a_hp_init_verbs[] = {
971 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
972 {}
973};
974
975
Takashi Iwai9230d212006-03-13 13:49:49 +0100976/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100977enum {
978 AD1986A_6STACK,
979 AD1986A_3STACK,
980 AD1986A_LAPTOP,
981 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200982 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +0100983 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +0100984 AD1986A_SAMSUNG,
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100985 AD1986A_MODELS
986};
Takashi Iwai9230d212006-03-13 13:49:49 +0100987
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100988static const char *ad1986a_models[AD1986A_MODELS] = {
989 [AD1986A_6STACK] = "6stack",
990 [AD1986A_3STACK] = "3stack",
991 [AD1986A_LAPTOP] = "laptop",
992 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200993 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +0100994 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +0100995 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100996};
997
998static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
999 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001000 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001001 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001002 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001003 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1004 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1005 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1006 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001007 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001008 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001009 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1010 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1011 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1012 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1013 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001014 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Takashi Iwai7db756f2007-12-24 14:36:09 +01001015 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
Tobin Davis18768992007-03-12 22:20:51 +01001016 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001017 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Tobin Davisf36090f2007-01-08 11:07:12 +01001018 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001019 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001020 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001021 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001022 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001023 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001024 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001025 {}
1026};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027
Takashi Iwaicb53c622007-08-10 17:21:45 +02001028#ifdef CONFIG_SND_HDA_POWER_SAVE
1029static struct hda_amp_list ad1986a_loopbacks[] = {
1030 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1031 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1032 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1033 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1034 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1035 { } /* end */
1036};
1037#endif
1038
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001039static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1040{
1041 unsigned int conf = snd_hda_codec_read(codec, nid, 0,
1042 AC_VERB_GET_CONFIG_DEFAULT, 0);
1043 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1044}
1045
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046static int patch_ad1986a(struct hda_codec *codec)
1047{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001048 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001049 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001051 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 if (spec == NULL)
1053 return -ENOMEM;
1054
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 codec->spec = spec;
1056
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001057 err = snd_hda_attach_beep_device(codec, 0x19);
1058 if (err < 0) {
1059 ad198x_free(codec);
1060 return err;
1061 }
1062 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1063
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 spec->multiout.max_channels = 6;
1065 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1066 spec->multiout.dac_nids = ad1986a_dac_nids;
1067 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001068 spec->num_adc_nids = 1;
1069 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001070 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001071 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001072 spec->num_mixers = 1;
1073 spec->mixers[0] = ad1986a_mixers;
1074 spec->num_init_verbs = 1;
1075 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001076#ifdef CONFIG_SND_HDA_POWER_SAVE
1077 spec->loopback.amplist = ad1986a_loopbacks;
1078#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001079 spec->vmaster_nid = 0x1b;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001081 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082
Takashi Iwai9230d212006-03-13 13:49:49 +01001083 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001084 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1085 ad1986a_models,
1086 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001087 switch (board_config) {
1088 case AD1986A_3STACK:
1089 spec->num_mixers = 2;
1090 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001091 spec->num_init_verbs = 2;
1092 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001093 spec->channel_mode = ad1986a_modes;
1094 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001095 spec->need_dac_fix = 1;
1096 spec->multiout.max_channels = 2;
1097 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001098 break;
1099 case AD1986A_LAPTOP:
1100 spec->mixers[0] = ad1986a_laptop_mixers;
1101 spec->multiout.max_channels = 2;
1102 spec->multiout.num_dacs = 1;
1103 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1104 break;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001105 case AD1986A_LAPTOP_EAPD:
1106 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001107 spec->num_init_verbs = 2;
1108 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1109 spec->multiout.max_channels = 2;
1110 spec->multiout.num_dacs = 1;
1111 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1112 if (!is_jack_available(codec, 0x25))
1113 spec->multiout.dig_out_nid = 0;
1114 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1115 break;
1116 case AD1986A_SAMSUNG:
1117 spec->mixers[0] = ad1986a_samsung_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001118 spec->num_init_verbs = 3;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001119 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001120 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001121 spec->multiout.max_channels = 2;
1122 spec->multiout.num_dacs = 1;
1123 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001124 if (!is_jack_available(codec, 0x25))
1125 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001126 spec->input_mux = &ad1986a_automic_capture_source;
1127 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1128 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001129 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001130 case AD1986A_LAPTOP_AUTOMUTE:
1131 spec->mixers[0] = ad1986a_laptop_automute_mixers;
1132 spec->num_init_verbs = 3;
1133 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1134 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1135 spec->multiout.max_channels = 2;
1136 spec->multiout.num_dacs = 1;
1137 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001138 if (!is_jack_available(codec, 0x25))
1139 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001140 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1141 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1142 codec->patch_ops.init = ad1986a_hp_init;
1143 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001144 case AD1986A_ULTRA:
1145 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1146 spec->num_init_verbs = 2;
1147 spec->init_verbs[1] = ad1986a_ultra_init;
1148 spec->multiout.max_channels = 2;
1149 spec->multiout.num_dacs = 1;
1150 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1151 spec->multiout.dig_out_nid = 0;
1152 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001153 }
1154
Takashi Iwaid29240c2007-10-26 12:35:56 +02001155 /* AD1986A has a hardware problem that it can't share a stream
1156 * with multiple output pins. The copy of front to surrounds
1157 * causes noisy or silent outputs at a certain timing, e.g.
1158 * changing the volume.
1159 * So, let's disable the shared stream.
1160 */
1161 spec->multiout.no_share_stream = 1;
1162
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 return 0;
1164}
1165
1166/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001167 * AD1983 specific
1168 */
1169
1170#define AD1983_SPDIF_OUT 0x02
1171#define AD1983_DAC 0x03
1172#define AD1983_ADC 0x04
1173
1174static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001175static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001176static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001177
1178static struct hda_input_mux ad1983_capture_source = {
1179 .num_items = 4,
1180 .items = {
1181 { "Mic", 0x0 },
1182 { "Line", 0x1 },
1183 { "Mix", 0x2 },
1184 { "Mix Mono", 0x3 },
1185 },
1186};
1187
1188/*
1189 * SPDIF playback route
1190 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001191static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001192{
1193 static char *texts[] = { "PCM", "ADC" };
1194
1195 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1196 uinfo->count = 1;
1197 uinfo->value.enumerated.items = 2;
1198 if (uinfo->value.enumerated.item > 1)
1199 uinfo->value.enumerated.item = 1;
1200 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1201 return 0;
1202}
1203
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001204static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001205{
1206 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1207 struct ad198x_spec *spec = codec->spec;
1208
1209 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1210 return 0;
1211}
1212
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001213static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001214{
1215 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1216 struct ad198x_spec *spec = codec->spec;
1217
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001218 if (ucontrol->value.enumerated.item[0] > 1)
1219 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001220 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1221 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001222 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1223 AC_VERB_SET_CONNECT_SEL,
1224 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001225 return 1;
1226 }
1227 return 0;
1228}
1229
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001230static struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001231 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1232 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1233 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1234 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1235 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1236 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1237 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1238 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1239 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1240 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1241 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1242 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001243 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
1244 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1245 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1246 {
1247 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1248 .name = "Capture Source",
1249 .info = ad198x_mux_enum_info,
1250 .get = ad198x_mux_enum_get,
1251 .put = ad198x_mux_enum_put,
1252 },
1253 {
1254 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001255 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001256 .info = ad1983_spdif_route_info,
1257 .get = ad1983_spdif_route_get,
1258 .put = ad1983_spdif_route_put,
1259 },
1260 { } /* end */
1261};
1262
1263static struct hda_verb ad1983_init_verbs[] = {
1264 /* Front, HP, Mono; mute as default */
1265 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1266 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1267 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1268 /* Beep, PCM, Mic, Line-In: mute */
1269 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1270 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1271 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1272 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1273 /* Front, HP selectors; from Mix */
1274 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1275 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1276 /* Mono selector; from Mix */
1277 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1278 /* Mic selector; Mic */
1279 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1280 /* Line-in selector: Line-in */
1281 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1282 /* Mic boost: 0dB */
1283 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1284 /* Record selector: mic */
1285 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1286 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1287 /* SPDIF route: PCM */
1288 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1289 /* Front Pin */
1290 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1291 /* HP Pin */
1292 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1293 /* Mono Pin */
1294 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1295 /* Mic Pin */
1296 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1297 /* Line Pin */
1298 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1299 { } /* end */
1300};
1301
Takashi Iwaicb53c622007-08-10 17:21:45 +02001302#ifdef CONFIG_SND_HDA_POWER_SAVE
1303static struct hda_amp_list ad1983_loopbacks[] = {
1304 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1305 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1306 { } /* end */
1307};
1308#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001309
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001310static int patch_ad1983(struct hda_codec *codec)
1311{
1312 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001313 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001314
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001315 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001316 if (spec == NULL)
1317 return -ENOMEM;
1318
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001319 codec->spec = spec;
1320
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001321 err = snd_hda_attach_beep_device(codec, 0x10);
1322 if (err < 0) {
1323 ad198x_free(codec);
1324 return err;
1325 }
1326 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1327
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001328 spec->multiout.max_channels = 2;
1329 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1330 spec->multiout.dac_nids = ad1983_dac_nids;
1331 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001332 spec->num_adc_nids = 1;
1333 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001334 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001335 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001336 spec->num_mixers = 1;
1337 spec->mixers[0] = ad1983_mixers;
1338 spec->num_init_verbs = 1;
1339 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001340 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001341#ifdef CONFIG_SND_HDA_POWER_SAVE
1342 spec->loopback.amplist = ad1983_loopbacks;
1343#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001344 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001345
1346 codec->patch_ops = ad198x_patch_ops;
1347
1348 return 0;
1349}
1350
1351
1352/*
1353 * AD1981 HD specific
1354 */
1355
1356#define AD1981_SPDIF_OUT 0x02
1357#define AD1981_DAC 0x03
1358#define AD1981_ADC 0x04
1359
1360static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001361static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001362static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001363
1364/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
1365static struct hda_input_mux ad1981_capture_source = {
1366 .num_items = 7,
1367 .items = {
1368 { "Front Mic", 0x0 },
1369 { "Line", 0x1 },
1370 { "Mix", 0x2 },
1371 { "Mix Mono", 0x3 },
1372 { "CD", 0x4 },
1373 { "Mic", 0x6 },
1374 { "Aux", 0x7 },
1375 },
1376};
1377
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001378static struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001379 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1380 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1381 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1382 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1383 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1384 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1385 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1386 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1387 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1388 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1389 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1390 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1391 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1392 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1393 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1394 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1395 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1396 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001397 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
1398 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
1399 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1400 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1401 {
1402 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1403 .name = "Capture Source",
1404 .info = ad198x_mux_enum_info,
1405 .get = ad198x_mux_enum_get,
1406 .put = ad198x_mux_enum_put,
1407 },
1408 /* identical with AD1983 */
1409 {
1410 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001411 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001412 .info = ad1983_spdif_route_info,
1413 .get = ad1983_spdif_route_get,
1414 .put = ad1983_spdif_route_put,
1415 },
1416 { } /* end */
1417};
1418
1419static struct hda_verb ad1981_init_verbs[] = {
1420 /* Front, HP, Mono; mute as default */
1421 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1422 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1423 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1424 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1425 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1426 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1427 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1428 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1429 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1430 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1431 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1432 /* Front, HP selectors; from Mix */
1433 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1434 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1435 /* Mono selector; from Mix */
1436 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1437 /* Mic Mixer; select Front Mic */
1438 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1439 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1440 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001441 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1442 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001443 /* Record selector: Front mic */
1444 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1445 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1446 /* SPDIF route: PCM */
1447 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1448 /* Front Pin */
1449 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1450 /* HP Pin */
1451 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1452 /* Mono Pin */
1453 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1454 /* Front & Rear Mic Pins */
1455 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1456 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1457 /* Line Pin */
1458 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1459 /* Digital Beep */
1460 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1461 /* Line-Out as Input: disabled */
1462 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1463 { } /* end */
1464};
1465
Takashi Iwaicb53c622007-08-10 17:21:45 +02001466#ifdef CONFIG_SND_HDA_POWER_SAVE
1467static struct hda_amp_list ad1981_loopbacks[] = {
1468 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1469 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1470 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1471 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1472 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1473 { } /* end */
1474};
1475#endif
1476
Takashi Iwai18a815d2006-03-01 19:54:39 +01001477/*
1478 * Patch for HP nx6320
1479 *
Tobin Davis18768992007-03-12 22:20:51 +01001480 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001481 * speaker output enabled _and_ mute-LED off.
1482 */
1483
1484#define AD1981_HP_EVENT 0x37
1485#define AD1981_MIC_EVENT 0x38
1486
1487static struct hda_verb ad1981_hp_init_verbs[] = {
1488 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1489 /* pin sensing on HP and Mic jacks */
1490 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1491 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1492 {}
1493};
1494
1495/* turn on/off EAPD (+ mute HP) as a master switch */
1496static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1497 struct snd_ctl_elem_value *ucontrol)
1498{
1499 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1500 struct ad198x_spec *spec = codec->spec;
1501
1502 if (! ad198x_eapd_put(kcontrol, ucontrol))
1503 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001504 /* change speaker pin appropriately */
1505 snd_hda_codec_write(codec, 0x05, 0,
1506 AC_VERB_SET_PIN_WIDGET_CONTROL,
1507 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001508 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001509 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1510 HDA_AMP_MUTE,
1511 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001512 return 1;
1513}
1514
1515/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02001516static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
1517 .ops = &snd_hda_bind_vol,
1518 .values = {
1519 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1520 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1521 0
1522 },
1523};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001524
1525/* mute internal speaker if HP is plugged */
1526static void ad1981_hp_automute(struct hda_codec *codec)
1527{
1528 unsigned int present;
1529
1530 present = snd_hda_codec_read(codec, 0x06, 0,
1531 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
Takashi Iwai47fd8302007-08-10 17:11:07 +02001532 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1533 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001534}
1535
1536/* toggle input of built-in and mic jack appropriately */
1537static void ad1981_hp_automic(struct hda_codec *codec)
1538{
1539 static struct hda_verb mic_jack_on[] = {
1540 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1541 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1542 {}
1543 };
1544 static struct hda_verb mic_jack_off[] = {
1545 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1546 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1547 {}
1548 };
1549 unsigned int present;
1550
1551 present = snd_hda_codec_read(codec, 0x08, 0,
1552 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1553 if (present)
1554 snd_hda_sequence_write(codec, mic_jack_on);
1555 else
1556 snd_hda_sequence_write(codec, mic_jack_off);
1557}
1558
1559/* unsolicited event for HP jack sensing */
1560static void ad1981_hp_unsol_event(struct hda_codec *codec,
1561 unsigned int res)
1562{
1563 res >>= 26;
1564 switch (res) {
1565 case AD1981_HP_EVENT:
1566 ad1981_hp_automute(codec);
1567 break;
1568 case AD1981_MIC_EVENT:
1569 ad1981_hp_automic(codec);
1570 break;
1571 }
1572}
1573
1574static struct hda_input_mux ad1981_hp_capture_source = {
1575 .num_items = 3,
1576 .items = {
1577 { "Mic", 0x0 },
1578 { "Docking-Station", 0x1 },
1579 { "Mix", 0x2 },
1580 },
1581};
1582
1583static struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001584 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001585 {
1586 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1587 .name = "Master Playback Switch",
1588 .info = ad198x_eapd_info,
1589 .get = ad198x_eapd_get,
1590 .put = ad1981_hp_master_sw_put,
1591 .private_value = 0x05,
1592 },
1593 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1594 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1595#if 0
1596 /* FIXME: analog mic/line loopback doesn't work with my tests...
1597 * (although recording is OK)
1598 */
1599 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1600 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1601 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1602 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1603 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1604 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1605 /* FIXME: does this laptop have analog CD connection? */
1606 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1607 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1608#endif
1609 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1610 HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
1611 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1612 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1613 {
1614 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1615 .name = "Capture Source",
1616 .info = ad198x_mux_enum_info,
1617 .get = ad198x_mux_enum_get,
1618 .put = ad198x_mux_enum_put,
1619 },
1620 { } /* end */
1621};
1622
1623/* initialize jack-sensing, too */
1624static int ad1981_hp_init(struct hda_codec *codec)
1625{
1626 ad198x_init(codec);
1627 ad1981_hp_automute(codec);
1628 ad1981_hp_automic(codec);
1629 return 0;
1630}
1631
Tobin Davis18768992007-03-12 22:20:51 +01001632/* configuration for Toshiba Laptops */
1633static struct hda_verb ad1981_toshiba_init_verbs[] = {
1634 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1635 /* pin sensing on HP and Mic jacks */
1636 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1637 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1638 {}
1639};
1640
1641static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
1642 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1643 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1644 { }
1645};
1646
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001647/* configuration for Lenovo Thinkpad T60 */
1648static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
1649 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1650 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1651 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1652 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1653 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1654 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1655 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1656 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1657 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1658 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1659 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1660 {
1661 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1662 .name = "Capture Source",
1663 .info = ad198x_mux_enum_info,
1664 .get = ad198x_mux_enum_get,
1665 .put = ad198x_mux_enum_put,
1666 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001667 /* identical with AD1983 */
1668 {
1669 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1670 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1671 .info = ad1983_spdif_route_info,
1672 .get = ad1983_spdif_route_get,
1673 .put = ad1983_spdif_route_put,
1674 },
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001675 { } /* end */
1676};
1677
1678static struct hda_input_mux ad1981_thinkpad_capture_source = {
1679 .num_items = 3,
1680 .items = {
1681 { "Mic", 0x0 },
1682 { "Mix", 0x2 },
1683 { "CD", 0x4 },
1684 },
1685};
1686
Takashi Iwai18a815d2006-03-01 19:54:39 +01001687/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001688enum {
1689 AD1981_BASIC,
1690 AD1981_HP,
1691 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001692 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001693 AD1981_MODELS
1694};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001695
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001696static const char *ad1981_models[AD1981_MODELS] = {
1697 [AD1981_HP] = "hp",
1698 [AD1981_THINKPAD] = "thinkpad",
1699 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001700 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001701};
1702
1703static struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001704 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001705 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001706 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001707 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001708 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001709 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001710 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001711 /* HP nx6320 (reversed SSID, H/W bug) */
1712 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001713 {}
1714};
1715
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001716static int patch_ad1981(struct hda_codec *codec)
1717{
1718 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001719 int err, board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001720
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001721 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001722 if (spec == NULL)
1723 return -ENOMEM;
1724
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001725 codec->spec = spec;
1726
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001727 err = snd_hda_attach_beep_device(codec, 0x10);
1728 if (err < 0) {
1729 ad198x_free(codec);
1730 return err;
1731 }
1732 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
1733
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001734 spec->multiout.max_channels = 2;
1735 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1736 spec->multiout.dac_nids = ad1981_dac_nids;
1737 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001738 spec->num_adc_nids = 1;
1739 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001740 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001741 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001742 spec->num_mixers = 1;
1743 spec->mixers[0] = ad1981_mixers;
1744 spec->num_init_verbs = 1;
1745 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001746 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001747#ifdef CONFIG_SND_HDA_POWER_SAVE
1748 spec->loopback.amplist = ad1981_loopbacks;
1749#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001750 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001751
1752 codec->patch_ops = ad198x_patch_ops;
1753
Takashi Iwai18a815d2006-03-01 19:54:39 +01001754 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001755 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1756 ad1981_models,
1757 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001758 switch (board_config) {
1759 case AD1981_HP:
1760 spec->mixers[0] = ad1981_hp_mixers;
1761 spec->num_init_verbs = 2;
1762 spec->init_verbs[1] = ad1981_hp_init_verbs;
1763 spec->multiout.dig_out_nid = 0;
1764 spec->input_mux = &ad1981_hp_capture_source;
1765
1766 codec->patch_ops.init = ad1981_hp_init;
1767 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1768 break;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001769 case AD1981_THINKPAD:
1770 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001771 spec->input_mux = &ad1981_thinkpad_capture_source;
1772 break;
Tobin Davis18768992007-03-12 22:20:51 +01001773 case AD1981_TOSHIBA:
1774 spec->mixers[0] = ad1981_hp_mixers;
1775 spec->mixers[1] = ad1981_toshiba_mixers;
1776 spec->num_init_verbs = 2;
1777 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1778 spec->multiout.dig_out_nid = 0;
1779 spec->input_mux = &ad1981_hp_capture_source;
1780 codec->patch_ops.init = ad1981_hp_init;
1781 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1782 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001783 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001784 return 0;
1785}
1786
1787
1788/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001789 * AD1988
1790 *
1791 * Output pins and routes
1792 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001793 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001794 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1795 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1796 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1797 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1798 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1799 * port-F 0x16 (mute) <- 0x2a <- 06
1800 * port-G 0x24 (mute) <- 0x27 <- 05
1801 * port-H 0x25 (mute) <- 0x28 <- 0a
1802 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1803 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001804 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1805 * (*) 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 +01001806 *
1807 * Input pins and routes
1808 *
1809 * pin boost mix input # / adc input #
1810 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1811 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1812 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1813 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1814 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1815 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1816 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1817 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1818 *
1819 *
1820 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001821 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001822 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001823 *
1824 * Inputs of Analog Mix (0x20)
1825 * 0:Port-B (front mic)
1826 * 1:Port-C/G/H (line-in)
1827 * 2:Port-A
1828 * 3:Port-D (line-in/2)
1829 * 4:Port-E/G/H (mic-in)
1830 * 5:Port-F (mic2-in)
1831 * 6:CD
1832 * 7:Beep
1833 *
1834 * ADC selection
1835 * 0:Port-A
1836 * 1:Port-B (front mic-in)
1837 * 2:Port-C (line-in)
1838 * 3:Port-F (mic2-in)
1839 * 4:Port-E (mic-in)
1840 * 5:CD
1841 * 6:Port-G
1842 * 7:Port-H
1843 * 8:Port-D (line-in/2)
1844 * 9:Mix
1845 *
1846 * Proposed pin assignments by the datasheet
1847 *
1848 * 6-stack
1849 * Port-A front headphone
1850 * B front mic-in
1851 * C rear line-in
1852 * D rear front-out
1853 * E rear mic-in
1854 * F rear surround
1855 * G rear CLFE
1856 * H rear side
1857 *
1858 * 3-stack
1859 * Port-A front headphone
1860 * B front mic
1861 * C rear line-in/surround
1862 * D rear front-out
1863 * E rear mic-in/CLFE
1864 *
1865 * laptop
1866 * Port-A headphone
1867 * B mic-in
1868 * C docking station
1869 * D internal speaker (with EAPD)
1870 * E/F quad mic array
1871 */
1872
1873
1874/* models */
1875enum {
1876 AD1988_6STACK,
1877 AD1988_6STACK_DIG,
1878 AD1988_3STACK,
1879 AD1988_3STACK_DIG,
1880 AD1988_LAPTOP,
1881 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01001882 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001883 AD1988_MODEL_LAST,
1884};
1885
Takashi Iwaid32410b12005-11-24 16:06:23 +01001886/* reivision id to check workarounds */
1887#define AD1988A_REV2 0x100200
1888
Takashi Iwai1a806f42006-07-03 15:58:16 +02001889#define is_rev2(codec) \
1890 ((codec)->vendor_id == 0x11d41988 && \
1891 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001892
1893/*
1894 * mixers
1895 */
1896
Takashi Iwaid32410b12005-11-24 16:06:23 +01001897static hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001898 0x04, 0x06, 0x05, 0x0a
1899};
1900
Takashi Iwaid32410b12005-11-24 16:06:23 +01001901static hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001902 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01001903};
1904
1905/* for AD1988A revision-2, DAC2-4 are swapped */
1906static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
1907 0x04, 0x05, 0x0a, 0x06
1908};
1909
1910static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001911 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01001912};
1913
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001914static hda_nid_t ad1988_adc_nids[3] = {
1915 0x08, 0x09, 0x0f
1916};
1917
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001918static hda_nid_t ad1988_capsrc_nids[3] = {
1919 0x0c, 0x0d, 0x0e
1920};
1921
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001922#define AD1988_SPDIF_OUT 0x02
1923#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001924#define AD1988_SPDIF_IN 0x07
1925
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001926static hda_nid_t ad1989b_slave_dig_outs[2] = {
1927 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI
1928};
1929
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001930static struct hda_input_mux ad1988_6stack_capture_source = {
1931 .num_items = 5,
1932 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001933 { "Front Mic", 0x1 }, /* port-B */
1934 { "Line", 0x2 }, /* port-C */
1935 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001936 { "CD", 0x5 },
1937 { "Mix", 0x9 },
1938 },
1939};
1940
1941static struct hda_input_mux ad1988_laptop_capture_source = {
1942 .num_items = 3,
1943 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001944 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001945 { "CD", 0x5 },
1946 { "Mix", 0x9 },
1947 },
1948};
1949
1950/*
1951 */
1952static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
1953 struct snd_ctl_elem_info *uinfo)
1954{
1955 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1956 struct ad198x_spec *spec = codec->spec;
1957 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
1958 spec->num_channel_mode);
1959}
1960
1961static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
1962 struct snd_ctl_elem_value *ucontrol)
1963{
1964 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1965 struct ad198x_spec *spec = codec->spec;
1966 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
1967 spec->num_channel_mode, spec->multiout.max_channels);
1968}
1969
1970static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
1971 struct snd_ctl_elem_value *ucontrol)
1972{
1973 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1974 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001975 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
1976 spec->num_channel_mode,
1977 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02001978 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02001979 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001980 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001981}
1982
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001983/* 6-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001984static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001985 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1986 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1987 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
1988 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
1989 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001990 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001991};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001992
Takashi Iwaid32410b12005-11-24 16:06:23 +01001993static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
1994 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1995 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1996 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
1997 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
1998 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001999 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002000};
2001
2002static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002003 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2004 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2005 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2006 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2007 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2008 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2009 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2010
2011 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2012 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2013 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2014 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2015 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2016 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2017 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2018 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2019
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002020 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002021 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2022
2023 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2024 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2025
2026 { } /* end */
2027};
2028
2029/* 3-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002030static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002031 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002032 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002033 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2034 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002035 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002036};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002037
Takashi Iwaid32410b12005-11-24 16:06:23 +01002038static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
2039 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002040 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2041 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2042 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002043 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002044};
2045
2046static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002047 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002048 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2049 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2050 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002051 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2052 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2053
2054 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2055 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2056 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2057 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2058 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2059 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2060 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2061 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2062
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002063 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002064 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2065
2066 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2067 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
2068 {
2069 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2070 .name = "Channel Mode",
2071 .info = ad198x_ch_mode_info,
2072 .get = ad198x_ch_mode_get,
2073 .put = ad198x_ch_mode_put,
2074 },
2075
2076 { } /* end */
2077};
2078
2079/* laptop mode */
2080static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
2081 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2082 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2083 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2084
2085 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2086 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2087 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2088 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2089 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2090 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2091
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002092 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002093 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2094
2095 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2096
2097 {
2098 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2099 .name = "External Amplifier",
Takashi Iwai18a815d2006-03-01 19:54:39 +01002100 .info = ad198x_eapd_info,
2101 .get = ad198x_eapd_get,
2102 .put = ad198x_eapd_put,
2103 .private_value = 0x12 | (1 << 8), /* port-D, inversed */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002104 },
2105
2106 { } /* end */
2107};
2108
2109/* capture */
2110static struct snd_kcontrol_new ad1988_capture_mixers[] = {
2111 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2112 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2113 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2114 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2115 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2116 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2117 {
2118 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2119 /* The multiple "Capture Source" controls confuse alsamixer
2120 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002121 */
2122 /* .name = "Capture Source", */
2123 .name = "Input Source",
2124 .count = 3,
2125 .info = ad198x_mux_enum_info,
2126 .get = ad198x_mux_enum_get,
2127 .put = ad198x_mux_enum_put,
2128 },
2129 { } /* end */
2130};
2131
2132static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2133 struct snd_ctl_elem_info *uinfo)
2134{
2135 static char *texts[] = {
2136 "PCM", "ADC1", "ADC2", "ADC3"
2137 };
2138 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2139 uinfo->count = 1;
2140 uinfo->value.enumerated.items = 4;
2141 if (uinfo->value.enumerated.item >= 4)
2142 uinfo->value.enumerated.item = 3;
2143 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2144 return 0;
2145}
2146
2147static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2148 struct snd_ctl_elem_value *ucontrol)
2149{
2150 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2151 unsigned int sel;
2152
Takashi Iwaibddcf542007-07-24 18:04:05 +02002153 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2154 AC_AMP_GET_INPUT);
2155 if (!(sel & 0x80))
2156 ucontrol->value.enumerated.item[0] = 0;
2157 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002158 sel = snd_hda_codec_read(codec, 0x0b, 0,
2159 AC_VERB_GET_CONNECT_SEL, 0);
2160 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002161 sel++;
2162 else
2163 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002164 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002165 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002166 return 0;
2167}
2168
2169static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2170 struct snd_ctl_elem_value *ucontrol)
2171{
2172 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002173 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002174 int change;
2175
Takashi Iwai35b26722007-05-05 12:17:17 +02002176 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002177 if (val > 3)
2178 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002179 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002180 sel = snd_hda_codec_read(codec, 0x1d, 0,
2181 AC_VERB_GET_AMP_GAIN_MUTE,
2182 AC_AMP_GET_INPUT);
2183 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002184 if (change) {
2185 snd_hda_codec_write_cache(codec, 0x1d, 0,
2186 AC_VERB_SET_AMP_GAIN_MUTE,
2187 AMP_IN_UNMUTE(0));
2188 snd_hda_codec_write_cache(codec, 0x1d, 0,
2189 AC_VERB_SET_AMP_GAIN_MUTE,
2190 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002191 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002192 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002193 sel = snd_hda_codec_read(codec, 0x1d, 0,
2194 AC_VERB_GET_AMP_GAIN_MUTE,
2195 AC_AMP_GET_INPUT | 0x01);
2196 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002197 if (change) {
2198 snd_hda_codec_write_cache(codec, 0x1d, 0,
2199 AC_VERB_SET_AMP_GAIN_MUTE,
2200 AMP_IN_MUTE(0));
2201 snd_hda_codec_write_cache(codec, 0x1d, 0,
2202 AC_VERB_SET_AMP_GAIN_MUTE,
2203 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002204 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002205 sel = snd_hda_codec_read(codec, 0x0b, 0,
2206 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2207 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002208 if (change)
2209 snd_hda_codec_write_cache(codec, 0x0b, 0,
2210 AC_VERB_SET_CONNECT_SEL,
2211 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002212 }
2213 return change;
2214}
2215
2216static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
2217 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2218 {
2219 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2220 .name = "IEC958 Playback Source",
2221 .info = ad1988_spdif_playback_source_info,
2222 .get = ad1988_spdif_playback_source_get,
2223 .put = ad1988_spdif_playback_source_put,
2224 },
2225 { } /* end */
2226};
2227
2228static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
2229 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2230 { } /* end */
2231};
2232
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002233static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
2234 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002235 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002236 { } /* end */
2237};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002238
2239/*
2240 * initialization verbs
2241 */
2242
2243/*
2244 * for 6-stack (+dig)
2245 */
2246static struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002247 /* Front, Surround, CLFE, side DAC; unmute as default */
2248 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2249 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2250 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2251 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002252 /* Port-A front headphon path */
2253 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2254 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2255 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2256 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2257 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2258 /* Port-D line-out path */
2259 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2260 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2261 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2262 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2263 /* Port-F surround path */
2264 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2265 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2266 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2267 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2268 /* Port-G CLFE path */
2269 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2270 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2271 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2272 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2273 /* Port-H side path */
2274 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2275 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2276 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2277 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2278 /* Mono out path */
2279 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2280 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2281 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2282 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2283 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2284 /* Port-B front mic-in path */
2285 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2286 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2287 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2288 /* Port-C line-in path */
2289 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2290 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2291 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2292 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2293 /* Port-E mic-in path */
2294 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2295 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2296 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2297 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002298 /* Analog CD Input */
2299 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002300 /* Analog Mix output amp */
2301 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002302
2303 { }
2304};
2305
2306static struct hda_verb ad1988_capture_init_verbs[] = {
2307 /* mute analog mix */
2308 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2309 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2310 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2311 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2312 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2313 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2314 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2315 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2316 /* select ADCs - front-mic */
2317 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2318 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2319 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002320
2321 { }
2322};
2323
2324static struct hda_verb ad1988_spdif_init_verbs[] = {
2325 /* SPDIF out sel */
2326 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2327 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2328 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002329 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002330 /* SPDIF out pin */
2331 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002332
2333 { }
2334};
2335
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002336/* AD1989 has no ADC -> SPDIF route */
2337static struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002338 /* SPDIF-1 out pin */
2339 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002340 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002341 /* SPDIF-2/HDMI out pin */
2342 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2343 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002344 { }
2345};
2346
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002347/*
2348 * verbs for 3stack (+dig)
2349 */
2350static struct hda_verb ad1988_3stack_ch2_init[] = {
2351 /* set port-C to line-in */
2352 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2353 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2354 /* set port-E to mic-in */
2355 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2356 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2357 { } /* end */
2358};
2359
2360static struct hda_verb ad1988_3stack_ch6_init[] = {
2361 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002362 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002363 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002364 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002365 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002366 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002367 { } /* end */
2368};
2369
2370static struct hda_channel_mode ad1988_3stack_modes[2] = {
2371 { 2, ad1988_3stack_ch2_init },
2372 { 6, ad1988_3stack_ch6_init },
2373};
2374
2375static struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002376 /* Front, Surround, CLFE, side DAC; unmute as default */
2377 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2378 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2379 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2380 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002381 /* Port-A front headphon path */
2382 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2383 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2384 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2385 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2386 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2387 /* Port-D line-out path */
2388 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2389 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2390 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2391 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2392 /* Mono out path */
2393 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2394 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2395 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2396 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2397 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2398 /* Port-B front mic-in path */
2399 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2400 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2401 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002402 /* Port-C line-in/surround path - 6ch mode as default */
2403 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2404 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002405 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002406 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002407 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002408 /* Port-E mic-in/CLFE path - 6ch mode as default */
2409 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2410 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002411 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002412 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002413 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2414 /* mute analog mix */
2415 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2416 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2417 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2418 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2419 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2420 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2421 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2422 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2423 /* select ADCs - front-mic */
2424 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2425 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2426 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002427 /* Analog Mix output amp */
2428 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002429 { }
2430};
2431
2432/*
2433 * verbs for laptop mode (+dig)
2434 */
2435static struct hda_verb ad1988_laptop_hp_on[] = {
2436 /* unmute port-A and mute port-D */
2437 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2438 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2439 { } /* end */
2440};
2441static struct hda_verb ad1988_laptop_hp_off[] = {
2442 /* mute port-A and unmute port-D */
2443 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2444 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2445 { } /* end */
2446};
2447
2448#define AD1988_HP_EVENT 0x01
2449
2450static struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002451 /* Front, Surround, CLFE, side DAC; unmute as default */
2452 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2453 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2454 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2455 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002456 /* Port-A front headphon path */
2457 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2458 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2459 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2460 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2461 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2462 /* unsolicited event for pin-sense */
2463 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2464 /* Port-D line-out path + EAPD */
2465 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2466 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2467 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2468 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2469 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2470 /* Mono out path */
2471 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2472 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2473 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2474 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2475 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2476 /* Port-B mic-in path */
2477 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2478 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2479 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2480 /* Port-C docking station - try to output */
2481 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2482 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2483 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2484 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2485 /* mute analog mix */
2486 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2487 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2488 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2489 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2490 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2491 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2492 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2493 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2494 /* select ADCs - mic */
2495 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2496 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2497 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002498 /* Analog Mix output amp */
2499 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002500 { }
2501};
2502
2503static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2504{
2505 if ((res >> 26) != AD1988_HP_EVENT)
2506 return;
2507 if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31))
2508 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2509 else
2510 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2511}
2512
Takashi Iwaicb53c622007-08-10 17:21:45 +02002513#ifdef CONFIG_SND_HDA_POWER_SAVE
2514static struct hda_amp_list ad1988_loopbacks[] = {
2515 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2516 { 0x20, HDA_INPUT, 1 }, /* Line */
2517 { 0x20, HDA_INPUT, 4 }, /* Mic */
2518 { 0x20, HDA_INPUT, 6 }, /* CD */
2519 { } /* end */
2520};
2521#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002522
2523/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002524 * Automatic parse of I/O pins from the BIOS configuration
2525 */
2526
Takashi Iwaid32410b12005-11-24 16:06:23 +01002527enum {
2528 AD_CTL_WIDGET_VOL,
2529 AD_CTL_WIDGET_MUTE,
2530 AD_CTL_BIND_MUTE,
2531};
2532static struct snd_kcontrol_new ad1988_control_templates[] = {
2533 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2534 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2535 HDA_BIND_MUTE(NULL, 0, 0, 0),
2536};
2537
2538/* add dynamic controls */
2539static int add_control(struct ad198x_spec *spec, int type, const char *name,
2540 unsigned long val)
2541{
2542 struct snd_kcontrol_new *knew;
2543
Takashi Iwai603c4012008-07-30 15:01:44 +02002544 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2545 knew = snd_array_new(&spec->kctls);
2546 if (!knew)
2547 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002548 *knew = ad1988_control_templates[type];
2549 knew->name = kstrdup(name, GFP_KERNEL);
2550 if (! knew->name)
2551 return -ENOMEM;
2552 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002553 return 0;
2554}
2555
2556#define AD1988_PIN_CD_NID 0x18
2557#define AD1988_PIN_BEEP_NID 0x10
2558
2559static hda_nid_t ad1988_mixer_nids[8] = {
2560 /* A B C D E F G H */
2561 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2562};
2563
2564static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2565{
2566 static hda_nid_t idx_to_dac[8] = {
2567 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002568 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002569 };
2570 static hda_nid_t idx_to_dac_rev2[8] = {
2571 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002572 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002573 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002574 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002575 return idx_to_dac_rev2[idx];
2576 else
2577 return idx_to_dac[idx];
2578}
2579
2580static hda_nid_t ad1988_boost_nids[8] = {
2581 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2582};
2583
2584static int ad1988_pin_idx(hda_nid_t nid)
2585{
2586 static hda_nid_t ad1988_io_pins[8] = {
2587 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2588 };
2589 int i;
2590 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2591 if (ad1988_io_pins[i] == nid)
2592 return i;
2593 return 0; /* should be -1 */
2594}
2595
2596static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2597{
2598 static int loopback_idx[8] = {
2599 2, 0, 1, 3, 4, 5, 1, 4
2600 };
2601 switch (nid) {
2602 case AD1988_PIN_CD_NID:
2603 return 6;
2604 default:
2605 return loopback_idx[ad1988_pin_idx(nid)];
2606 }
2607}
2608
2609static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2610{
2611 static int adc_idx[8] = {
2612 0, 1, 2, 8, 4, 3, 6, 7
2613 };
2614 switch (nid) {
2615 case AD1988_PIN_CD_NID:
2616 return 5;
2617 default:
2618 return adc_idx[ad1988_pin_idx(nid)];
2619 }
2620}
2621
2622/* fill in the dac_nids table from the parsed pin configuration */
2623static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2624 const struct auto_pin_cfg *cfg)
2625{
2626 struct ad198x_spec *spec = codec->spec;
2627 int i, idx;
2628
2629 spec->multiout.dac_nids = spec->private_dac_nids;
2630
2631 /* check the pins hardwired to audio widget */
2632 for (i = 0; i < cfg->line_outs; i++) {
2633 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2634 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2635 }
2636 spec->multiout.num_dacs = cfg->line_outs;
2637 return 0;
2638}
2639
2640/* add playback controls from the parsed DAC table */
2641static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2642 const struct auto_pin_cfg *cfg)
2643{
2644 char name[32];
2645 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2646 hda_nid_t nid;
2647 int i, err;
2648
2649 for (i = 0; i < cfg->line_outs; i++) {
2650 hda_nid_t dac = spec->multiout.dac_nids[i];
2651 if (! dac)
2652 continue;
2653 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2654 if (i == 2) {
2655 /* Center/LFE */
2656 err = add_control(spec, AD_CTL_WIDGET_VOL,
2657 "Center Playback Volume",
2658 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2659 if (err < 0)
2660 return err;
2661 err = add_control(spec, AD_CTL_WIDGET_VOL,
2662 "LFE Playback Volume",
2663 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2664 if (err < 0)
2665 return err;
2666 err = add_control(spec, AD_CTL_BIND_MUTE,
2667 "Center Playback Switch",
2668 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2669 if (err < 0)
2670 return err;
2671 err = add_control(spec, AD_CTL_BIND_MUTE,
2672 "LFE Playback Switch",
2673 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2674 if (err < 0)
2675 return err;
2676 } else {
2677 sprintf(name, "%s Playback Volume", chname[i]);
2678 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2679 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2680 if (err < 0)
2681 return err;
2682 sprintf(name, "%s Playback Switch", chname[i]);
2683 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2684 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2685 if (err < 0)
2686 return err;
2687 }
2688 }
2689 return 0;
2690}
2691
2692/* add playback controls for speaker and HP outputs */
2693static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2694 const char *pfx)
2695{
2696 struct ad198x_spec *spec = codec->spec;
2697 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02002698 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002699 char name[32];
2700
2701 if (! pin)
2702 return 0;
2703
2704 idx = ad1988_pin_idx(pin);
2705 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02002706 /* check whether the corresponding DAC was already taken */
2707 for (i = 0; i < spec->autocfg.line_outs; i++) {
2708 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2709 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
2710 if (dac == nid)
2711 break;
2712 }
2713 if (i >= spec->autocfg.line_outs) {
2714 /* specify the DAC as the extra output */
2715 if (!spec->multiout.hp_nid)
2716 spec->multiout.hp_nid = nid;
2717 else
2718 spec->multiout.extra_out_nid[0] = nid;
2719 /* control HP volume/switch on the output mixer amp */
2720 sprintf(name, "%s Playback Volume", pfx);
2721 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2722 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2723 if (err < 0)
2724 return err;
2725 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01002726 nid = ad1988_mixer_nids[idx];
2727 sprintf(name, "%s Playback Switch", pfx);
2728 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2729 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2730 return err;
2731 return 0;
2732}
2733
2734/* create input playback/capture controls for the given pin */
2735static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2736 const char *ctlname, int boost)
2737{
2738 char name[32];
2739 int err, idx;
2740
2741 sprintf(name, "%s Playback Volume", ctlname);
2742 idx = ad1988_pin_to_loopback_idx(pin);
2743 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2744 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2745 return err;
2746 sprintf(name, "%s Playback Switch", ctlname);
2747 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2748 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2749 return err;
2750 if (boost) {
2751 hda_nid_t bnid;
2752 idx = ad1988_pin_idx(pin);
2753 bnid = ad1988_boost_nids[idx];
2754 if (bnid) {
2755 sprintf(name, "%s Boost", ctlname);
2756 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2757 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2758
2759 }
2760 }
2761 return 0;
2762}
2763
2764/* create playback/capture controls for input pins */
2765static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2766 const struct auto_pin_cfg *cfg)
2767{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002768 struct hda_input_mux *imux = &spec->private_imux;
2769 int i, err;
2770
2771 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002772 err = new_analog_input(spec, cfg->input_pins[i],
2773 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002774 i <= AUTO_PIN_FRONT_MIC);
2775 if (err < 0)
2776 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002777 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002778 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2779 imux->num_items++;
2780 }
2781 imux->items[imux->num_items].label = "Mix";
2782 imux->items[imux->num_items].index = 9;
2783 imux->num_items++;
2784
2785 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2786 "Analog Mix Playback Volume",
2787 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2788 return err;
2789 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2790 "Analog Mix Playback Switch",
2791 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2792 return err;
2793
2794 return 0;
2795}
2796
2797static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2798 hda_nid_t nid, int pin_type,
2799 int dac_idx)
2800{
2801 /* set as output */
2802 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2803 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2804 switch (nid) {
2805 case 0x11: /* port-A - DAC 04 */
2806 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2807 break;
2808 case 0x14: /* port-B - DAC 06 */
2809 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2810 break;
2811 case 0x15: /* port-C - DAC 05 */
2812 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2813 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002814 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002815 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2816 break;
2817 case 0x13: /* mono - DAC 04 */
2818 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2819 break;
2820 }
2821}
2822
2823static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2824{
2825 struct ad198x_spec *spec = codec->spec;
2826 int i;
2827
2828 for (i = 0; i < spec->autocfg.line_outs; i++) {
2829 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2830 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2831 }
2832}
2833
2834static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2835{
2836 struct ad198x_spec *spec = codec->spec;
2837 hda_nid_t pin;
2838
Takashi Iwai82bc9552006-03-21 11:24:42 +01002839 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002840 if (pin) /* connect to front */
2841 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002842 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002843 if (pin) /* connect to front */
2844 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2845}
2846
2847static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2848{
2849 struct ad198x_spec *spec = codec->spec;
2850 int i, idx;
2851
2852 for (i = 0; i < AUTO_PIN_LAST; i++) {
2853 hda_nid_t nid = spec->autocfg.input_pins[i];
2854 if (! nid)
2855 continue;
2856 switch (nid) {
2857 case 0x15: /* port-C */
2858 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2859 break;
2860 case 0x17: /* port-E */
2861 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2862 break;
2863 }
2864 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2865 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2866 if (nid != AD1988_PIN_CD_NID)
2867 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
2868 AMP_OUT_MUTE);
2869 idx = ad1988_pin_idx(nid);
2870 if (ad1988_boost_nids[idx])
2871 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
2872 AC_VERB_SET_AMP_GAIN_MUTE,
2873 AMP_OUT_ZERO);
2874 }
2875}
2876
2877/* parse the BIOS configuration and set up the alc_spec */
2878/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
2879static int ad1988_parse_auto_config(struct hda_codec *codec)
2880{
2881 struct ad198x_spec *spec = codec->spec;
2882 int err;
2883
Kailang Yangdf694da2005-12-05 19:42:22 +01002884 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002885 return err;
2886 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
2887 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002888 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002889 return 0; /* can't find valid BIOS pin config */
2890 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01002891 (err = ad1988_auto_create_extra_out(codec,
2892 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002893 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002894 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002895 "Headphone")) < 0 ||
2896 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
2897 return err;
2898
2899 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2900
2901 if (spec->autocfg.dig_out_pin)
2902 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2903 if (spec->autocfg.dig_in_pin)
2904 spec->dig_in_nid = AD1988_SPDIF_IN;
2905
Takashi Iwai603c4012008-07-30 15:01:44 +02002906 if (spec->kctls.list)
2907 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002908
2909 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
2910
2911 spec->input_mux = &spec->private_imux;
2912
2913 return 1;
2914}
2915
2916/* init callback for auto-configuration model -- overriding the default init */
2917static int ad1988_auto_init(struct hda_codec *codec)
2918{
2919 ad198x_init(codec);
2920 ad1988_auto_init_multi_out(codec);
2921 ad1988_auto_init_extra_out(codec);
2922 ad1988_auto_init_analog_input(codec);
2923 return 0;
2924}
2925
2926
2927/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002928 */
2929
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002930static const char *ad1988_models[AD1988_MODEL_LAST] = {
2931 [AD1988_6STACK] = "6stack",
2932 [AD1988_6STACK_DIG] = "6stack-dig",
2933 [AD1988_3STACK] = "3stack",
2934 [AD1988_3STACK_DIG] = "3stack-dig",
2935 [AD1988_LAPTOP] = "laptop",
2936 [AD1988_LAPTOP_DIG] = "laptop-dig",
2937 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002938};
2939
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002940static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002941 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002942 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02002943 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07002944 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002945 {}
2946};
2947
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002948static int patch_ad1988(struct hda_codec *codec)
2949{
2950 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002951 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002952
2953 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2954 if (spec == NULL)
2955 return -ENOMEM;
2956
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002957 codec->spec = spec;
2958
Takashi Iwai1a806f42006-07-03 15:58:16 +02002959 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002960 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
2961
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002962 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002963 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002964 if (board_config < 0) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002965 printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n");
2966 board_config = AD1988_AUTO;
2967 }
2968
2969 if (board_config == AD1988_AUTO) {
2970 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002971 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002972 if (err < 0) {
2973 ad198x_free(codec);
2974 return err;
2975 } else if (! err) {
2976 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
2977 board_config = AD1988_6STACK;
2978 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002979 }
2980
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002981 err = snd_hda_attach_beep_device(codec, 0x10);
2982 if (err < 0) {
2983 ad198x_free(codec);
2984 return err;
2985 }
2986 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
2987
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002988 switch (board_config) {
2989 case AD1988_6STACK:
2990 case AD1988_6STACK_DIG:
2991 spec->multiout.max_channels = 8;
2992 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002993 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002994 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
2995 else
2996 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002997 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002998 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002999 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003000 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3001 else
3002 spec->mixers[0] = ad1988_6stack_mixers1;
3003 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003004 spec->num_init_verbs = 1;
3005 spec->init_verbs[0] = ad1988_6stack_init_verbs;
3006 if (board_config == AD1988_6STACK_DIG) {
3007 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3008 spec->dig_in_nid = AD1988_SPDIF_IN;
3009 }
3010 break;
3011 case AD1988_3STACK:
3012 case AD1988_3STACK_DIG:
3013 spec->multiout.max_channels = 6;
3014 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003015 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003016 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3017 else
3018 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003019 spec->input_mux = &ad1988_6stack_capture_source;
3020 spec->channel_mode = ad1988_3stack_modes;
3021 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003022 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003023 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003024 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3025 else
3026 spec->mixers[0] = ad1988_3stack_mixers1;
3027 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003028 spec->num_init_verbs = 1;
3029 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3030 if (board_config == AD1988_3STACK_DIG)
3031 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3032 break;
3033 case AD1988_LAPTOP:
3034 case AD1988_LAPTOP_DIG:
3035 spec->multiout.max_channels = 2;
3036 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003037 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003038 spec->input_mux = &ad1988_laptop_capture_source;
3039 spec->num_mixers = 1;
3040 spec->mixers[0] = ad1988_laptop_mixers;
3041 spec->num_init_verbs = 1;
3042 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3043 if (board_config == AD1988_LAPTOP_DIG)
3044 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3045 break;
3046 }
3047
Takashi Iwaid32410b12005-11-24 16:06:23 +01003048 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3049 spec->adc_nids = ad1988_adc_nids;
3050 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003051 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3052 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3053 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003054 if (codec->vendor_id >= 0x11d4989a) {
3055 spec->mixers[spec->num_mixers++] =
3056 ad1989_spdif_out_mixers;
3057 spec->init_verbs[spec->num_init_verbs++] =
3058 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003059 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003060 } else {
3061 spec->mixers[spec->num_mixers++] =
3062 ad1988_spdif_out_mixers;
3063 spec->init_verbs[spec->num_init_verbs++] =
3064 ad1988_spdif_init_verbs;
3065 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003066 }
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003067 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003068 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
3069
3070 codec->patch_ops = ad198x_patch_ops;
3071 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003072 case AD1988_AUTO:
3073 codec->patch_ops.init = ad1988_auto_init;
3074 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003075 case AD1988_LAPTOP:
3076 case AD1988_LAPTOP_DIG:
3077 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3078 break;
3079 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003080#ifdef CONFIG_SND_HDA_POWER_SAVE
3081 spec->loopback.amplist = ad1988_loopbacks;
3082#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003083 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003084
3085 return 0;
3086}
3087
3088
3089/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003090 * AD1884 / AD1984
3091 *
3092 * port-B - front line/mic-in
3093 * port-E - aux in/out
3094 * port-F - aux in/out
3095 * port-C - rear line/mic-in
3096 * port-D - rear line/hp-out
3097 * port-A - front line/hp-out
3098 *
3099 * AD1984 = AD1884 + two digital mic-ins
3100 *
3101 * FIXME:
3102 * For simplicity, we share the single DAC for both HP and line-outs
3103 * right now. The inidividual playbacks could be easily implemented,
3104 * but no build-up framework is given, so far.
3105 */
3106
3107static hda_nid_t ad1884_dac_nids[1] = {
3108 0x04,
3109};
3110
3111static hda_nid_t ad1884_adc_nids[2] = {
3112 0x08, 0x09,
3113};
3114
3115static hda_nid_t ad1884_capsrc_nids[2] = {
3116 0x0c, 0x0d,
3117};
3118
3119#define AD1884_SPDIF_OUT 0x02
3120
3121static struct hda_input_mux ad1884_capture_source = {
3122 .num_items = 4,
3123 .items = {
3124 { "Front Mic", 0x0 },
3125 { "Mic", 0x1 },
3126 { "CD", 0x2 },
3127 { "Mix", 0x3 },
3128 },
3129};
3130
3131static struct snd_kcontrol_new ad1884_base_mixers[] = {
3132 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3133 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3134 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3135 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3136 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3137 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3138 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3139 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3140 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3141 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3142 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3143 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003144 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
3145 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3146 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3147 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3148 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3149 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3150 {
3151 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3152 /* The multiple "Capture Source" controls confuse alsamixer
3153 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003154 */
3155 /* .name = "Capture Source", */
3156 .name = "Input Source",
3157 .count = 2,
3158 .info = ad198x_mux_enum_info,
3159 .get = ad198x_mux_enum_get,
3160 .put = ad198x_mux_enum_put,
3161 },
3162 /* SPDIF controls */
3163 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3164 {
3165 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3166 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3167 /* identical with ad1983 */
3168 .info = ad1983_spdif_route_info,
3169 .get = ad1983_spdif_route_get,
3170 .put = ad1983_spdif_route_put,
3171 },
3172 { } /* end */
3173};
3174
3175static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3176 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3177 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3178 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003179 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003180 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003181 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003182 { } /* end */
3183};
3184
3185/*
3186 * initialization verbs
3187 */
3188static struct hda_verb ad1884_init_verbs[] = {
3189 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003190 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3191 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003192 /* Port-A (HP) mixer */
3193 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3194 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3195 /* Port-A pin */
3196 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3197 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3198 /* HP selector - select DAC2 */
3199 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3200 /* Port-D (Line-out) mixer */
3201 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3202 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3203 /* Port-D pin */
3204 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3205 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3206 /* Mono-out mixer */
3207 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3208 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3209 /* Mono-out pin */
3210 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3211 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3212 /* Mono selector */
3213 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3214 /* Port-B (front mic) pin */
3215 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003216 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003217 /* Port-C (rear mic) pin */
3218 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003219 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003220 /* Analog mixer; mute as default */
3221 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3222 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3223 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3224 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3225 /* Analog Mix output amp */
3226 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3227 /* SPDIF output selector */
3228 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3229 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3230 { } /* end */
3231};
3232
Takashi Iwaicb53c622007-08-10 17:21:45 +02003233#ifdef CONFIG_SND_HDA_POWER_SAVE
3234static struct hda_amp_list ad1884_loopbacks[] = {
3235 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3236 { 0x20, HDA_INPUT, 1 }, /* Mic */
3237 { 0x20, HDA_INPUT, 2 }, /* CD */
3238 { 0x20, HDA_INPUT, 4 }, /* Docking */
3239 { } /* end */
3240};
3241#endif
3242
Takashi Iwai2134ea42008-01-10 16:53:55 +01003243static const char *ad1884_slave_vols[] = {
3244 "PCM Playback Volume",
3245 "Mic Playback Volume",
3246 "Mono Playback Volume",
3247 "Front Mic Playback Volume",
3248 "Mic Playback Volume",
3249 "CD Playback Volume",
3250 "Internal Mic Playback Volume",
3251 "Docking Mic Playback Volume"
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003252 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003253 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003254 NULL
3255};
3256
Takashi Iwai2bac6472007-05-18 18:21:41 +02003257static int patch_ad1884(struct hda_codec *codec)
3258{
3259 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003260 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003261
3262 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3263 if (spec == NULL)
3264 return -ENOMEM;
3265
Takashi Iwai2bac6472007-05-18 18:21:41 +02003266 codec->spec = spec;
3267
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003268 err = snd_hda_attach_beep_device(codec, 0x10);
3269 if (err < 0) {
3270 ad198x_free(codec);
3271 return err;
3272 }
3273 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3274
Takashi Iwai2bac6472007-05-18 18:21:41 +02003275 spec->multiout.max_channels = 2;
3276 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3277 spec->multiout.dac_nids = ad1884_dac_nids;
3278 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3279 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3280 spec->adc_nids = ad1884_adc_nids;
3281 spec->capsrc_nids = ad1884_capsrc_nids;
3282 spec->input_mux = &ad1884_capture_source;
3283 spec->num_mixers = 1;
3284 spec->mixers[0] = ad1884_base_mixers;
3285 spec->num_init_verbs = 1;
3286 spec->init_verbs[0] = ad1884_init_verbs;
3287 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003288#ifdef CONFIG_SND_HDA_POWER_SAVE
3289 spec->loopback.amplist = ad1884_loopbacks;
3290#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003291 spec->vmaster_nid = 0x04;
3292 /* we need to cover all playback volumes */
3293 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003294
3295 codec->patch_ops = ad198x_patch_ops;
3296
3297 return 0;
3298}
3299
3300/*
3301 * Lenovo Thinkpad T61/X61
3302 */
3303static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003304 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003305 .items = {
3306 { "Mic", 0x0 },
3307 { "Internal Mic", 0x1 },
3308 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003309 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003310 },
3311};
3312
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003313
3314/*
3315 * Dell Precision T3400
3316 */
3317static struct hda_input_mux ad1984_dell_desktop_capture_source = {
3318 .num_items = 3,
3319 .items = {
3320 { "Front Mic", 0x0 },
3321 { "Line-In", 0x1 },
3322 { "Mix", 0x3 },
3323 },
3324};
3325
3326
Takashi Iwai2bac6472007-05-18 18:21:41 +02003327static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3328 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3329 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3330 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3331 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3332 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3333 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3334 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3335 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3336 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3337 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003338 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3339 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003340 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003341 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3342 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3343 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3344 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3345 {
3346 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3347 /* The multiple "Capture Source" controls confuse alsamixer
3348 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003349 */
3350 /* .name = "Capture Source", */
3351 .name = "Input Source",
3352 .count = 2,
3353 .info = ad198x_mux_enum_info,
3354 .get = ad198x_mux_enum_get,
3355 .put = ad198x_mux_enum_put,
3356 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003357 /* SPDIF controls */
3358 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3359 {
3360 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3361 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3362 /* identical with ad1983 */
3363 .info = ad1983_spdif_route_info,
3364 .get = ad1983_spdif_route_get,
3365 .put = ad1983_spdif_route_put,
3366 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003367 { } /* end */
3368};
3369
3370/* additional verbs */
3371static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3372 /* Port-E (docking station mic) pin */
3373 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3374 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3375 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003376 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003377 /* Analog mixer - docking mic; mute as default */
3378 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003379 /* enable EAPD bit */
3380 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003381 { } /* end */
3382};
3383
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003384/*
3385 * Dell Precision T3400
3386 */
3387static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
3388 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3389 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3390 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3391 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3392 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3393 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3394 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3395 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3396 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003397 HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
3398 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3399 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3400 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3401 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3402 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3403 {
3404 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3405 /* The multiple "Capture Source" controls confuse alsamixer
3406 * So call somewhat different..
3407 */
3408 /* .name = "Capture Source", */
3409 .name = "Input Source",
3410 .count = 2,
3411 .info = ad198x_mux_enum_info,
3412 .get = ad198x_mux_enum_get,
3413 .put = ad198x_mux_enum_put,
3414 },
3415 { } /* end */
3416};
3417
Takashi Iwai2bac6472007-05-18 18:21:41 +02003418/* Digial MIC ADC NID 0x05 + 0x06 */
3419static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3420 struct hda_codec *codec,
3421 unsigned int stream_tag,
3422 unsigned int format,
3423 struct snd_pcm_substream *substream)
3424{
3425 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3426 stream_tag, 0, format);
3427 return 0;
3428}
3429
3430static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3431 struct hda_codec *codec,
3432 struct snd_pcm_substream *substream)
3433{
Takashi Iwai888afa12008-03-18 09:57:50 +01003434 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003435 return 0;
3436}
3437
3438static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3439 .substreams = 2,
3440 .channels_min = 2,
3441 .channels_max = 2,
3442 .nid = 0x05,
3443 .ops = {
3444 .prepare = ad1984_pcm_dmic_prepare,
3445 .cleanup = ad1984_pcm_dmic_cleanup
3446 },
3447};
3448
3449static int ad1984_build_pcms(struct hda_codec *codec)
3450{
3451 struct ad198x_spec *spec = codec->spec;
3452 struct hda_pcm *info;
3453 int err;
3454
3455 err = ad198x_build_pcms(codec);
3456 if (err < 0)
3457 return err;
3458
3459 info = spec->pcm_rec + codec->num_pcms;
3460 codec->num_pcms++;
3461 info->name = "AD1984 Digital Mic";
3462 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3463 return 0;
3464}
3465
3466/* models */
3467enum {
3468 AD1984_BASIC,
3469 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003470 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003471 AD1984_MODELS
3472};
3473
3474static const char *ad1984_models[AD1984_MODELS] = {
3475 [AD1984_BASIC] = "basic",
3476 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003477 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003478};
3479
3480static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3481 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003482 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003483 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003484 {}
3485};
3486
3487static int patch_ad1984(struct hda_codec *codec)
3488{
3489 struct ad198x_spec *spec;
3490 int board_config, err;
3491
3492 err = patch_ad1884(codec);
3493 if (err < 0)
3494 return err;
3495 spec = codec->spec;
3496 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3497 ad1984_models, ad1984_cfg_tbl);
3498 switch (board_config) {
3499 case AD1984_BASIC:
3500 /* additional digital mics */
3501 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3502 codec->patch_ops.build_pcms = ad1984_build_pcms;
3503 break;
3504 case AD1984_THINKPAD:
Jerone Youngebf00c52008-01-07 12:22:18 +01003505 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003506 spec->input_mux = &ad1984_thinkpad_capture_source;
3507 spec->mixers[0] = ad1984_thinkpad_mixers;
3508 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
3509 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003510 case AD1984_DELL_DESKTOP:
3511 spec->multiout.dig_out_nid = 0;
3512 spec->input_mux = &ad1984_dell_desktop_capture_source;
3513 spec->mixers[0] = ad1984_dell_desktop_mixers;
3514 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003515 }
3516 return 0;
3517}
3518
3519
3520/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003521 * AD1883 / AD1884A / AD1984A / AD1984B
3522 *
3523 * port-B (0x14) - front mic-in
3524 * port-E (0x1c) - rear mic-in
3525 * port-F (0x16) - CD / ext out
3526 * port-C (0x15) - rear line-in
3527 * port-D (0x12) - rear line-out
3528 * port-A (0x11) - front hp-out
3529 *
3530 * AD1984A = AD1884A + digital-mic
3531 * AD1883 = equivalent with AD1984A
3532 * AD1984B = AD1984A + extra SPDIF-out
3533 *
3534 * FIXME:
3535 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3536 */
3537
3538static hda_nid_t ad1884a_dac_nids[1] = {
3539 0x03,
3540};
3541
3542#define ad1884a_adc_nids ad1884_adc_nids
3543#define ad1884a_capsrc_nids ad1884_capsrc_nids
3544
3545#define AD1884A_SPDIF_OUT 0x02
3546
3547static struct hda_input_mux ad1884a_capture_source = {
3548 .num_items = 5,
3549 .items = {
3550 { "Front Mic", 0x0 },
3551 { "Mic", 0x4 },
3552 { "Line", 0x1 },
3553 { "CD", 0x2 },
3554 { "Mix", 0x3 },
3555 },
3556};
3557
3558static struct snd_kcontrol_new ad1884a_base_mixers[] = {
3559 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3560 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3561 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3562 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3563 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3564 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3565 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3566 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3567 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3568 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3569 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3570 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3571 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3572 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3573 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3574 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003575 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3576 HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
3577 HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3578 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3579 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3580 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3581 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3582 {
3583 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3584 /* The multiple "Capture Source" controls confuse alsamixer
3585 * So call somewhat different..
3586 */
3587 /* .name = "Capture Source", */
3588 .name = "Input Source",
3589 .count = 2,
3590 .info = ad198x_mux_enum_info,
3591 .get = ad198x_mux_enum_get,
3592 .put = ad198x_mux_enum_put,
3593 },
3594 /* SPDIF controls */
3595 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3596 {
3597 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3598 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3599 /* identical with ad1983 */
3600 .info = ad1983_spdif_route_info,
3601 .get = ad1983_spdif_route_get,
3602 .put = ad1983_spdif_route_put,
3603 },
3604 { } /* end */
3605};
3606
3607/*
3608 * initialization verbs
3609 */
3610static struct hda_verb ad1884a_init_verbs[] = {
3611 /* DACs; unmute as default */
3612 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3613 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3614 /* Port-A (HP) mixer - route only from analog mixer */
3615 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3616 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3617 /* Port-A pin */
3618 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3619 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3620 /* Port-D (Line-out) mixer - route only from analog mixer */
3621 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3622 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3623 /* Port-D pin */
3624 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3625 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3626 /* Mono-out mixer - route only from analog mixer */
3627 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3628 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3629 /* Mono-out pin */
3630 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3631 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3632 /* Port-B (front mic) pin */
3633 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003634 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003635 /* Port-C (rear line-in) pin */
3636 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003637 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003638 /* Port-E (rear mic) pin */
3639 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3640 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3641 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
3642 /* Port-F (CD) pin */
3643 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3644 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3645 /* Analog mixer; mute as default */
3646 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3647 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3648 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3649 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3650 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
3651 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3652 /* Analog Mix output amp */
3653 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3654 /* capture sources */
3655 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
3656 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3657 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3658 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3659 /* SPDIF output amp */
3660 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3661 { } /* end */
3662};
3663
3664#ifdef CONFIG_SND_HDA_POWER_SAVE
3665static struct hda_amp_list ad1884a_loopbacks[] = {
3666 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3667 { 0x20, HDA_INPUT, 1 }, /* Mic */
3668 { 0x20, HDA_INPUT, 2 }, /* CD */
3669 { 0x20, HDA_INPUT, 4 }, /* Docking */
3670 { } /* end */
3671};
3672#endif
3673
3674/*
3675 * Laptop model
3676 *
3677 * Port A: Headphone jack
3678 * Port B: MIC jack
3679 * Port C: Internal MIC
3680 * Port D: Dock Line Out (if enabled)
3681 * Port E: Dock Line In (if enabled)
3682 * Port F: Internal speakers
3683 */
3684
3685static struct hda_input_mux ad1884a_laptop_capture_source = {
3686 .num_items = 4,
3687 .items = {
3688 { "Mic", 0x0 }, /* port-B */
3689 { "Internal Mic", 0x1 }, /* port-C */
3690 { "Dock Mic", 0x4 }, /* port-E */
3691 { "Mix", 0x3 },
3692 },
3693};
3694
3695static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
3696 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3697 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3698 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3699 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3700 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3701 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3702 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3703 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3704 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3705 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3706 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003707 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3708 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
3709 HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
3710 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3711 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3712 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3713 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3714 {
3715 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3716 /* The multiple "Capture Source" controls confuse alsamixer
3717 * So call somewhat different..
3718 */
3719 /* .name = "Capture Source", */
3720 .name = "Input Source",
3721 .count = 2,
3722 .info = ad198x_mux_enum_info,
3723 .get = ad198x_mux_enum_get,
3724 .put = ad198x_mux_enum_put,
3725 },
3726 { } /* end */
3727};
3728
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003729static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
3730 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3731 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3732 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3733 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02003734 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
3735 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003736 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3737 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003738 { } /* end */
3739};
3740
Takashi Iwaic5059252008-02-16 09:43:56 +01003741/* mute internal speaker if HP is plugged */
3742static void ad1884a_hp_automute(struct hda_codec *codec)
3743{
3744 unsigned int present;
3745
3746 present = snd_hda_codec_read(codec, 0x11, 0,
3747 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
3748 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3749 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3750 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3751 present ? 0x00 : 0x02);
3752}
3753
Takashi Iwai269ef192008-05-30 15:32:15 +02003754/* switch to external mic if plugged */
3755static void ad1884a_hp_automic(struct hda_codec *codec)
3756{
3757 unsigned int present;
3758
3759 present = snd_hda_codec_read(codec, 0x14, 0,
3760 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
3761 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
3762 present ? 0 : 1);
3763}
3764
Takashi Iwaic5059252008-02-16 09:43:56 +01003765#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02003766#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01003767
3768/* unsolicited event for HP jack sensing */
3769static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
3770{
Takashi Iwai269ef192008-05-30 15:32:15 +02003771 switch (res >> 26) {
3772 case AD1884A_HP_EVENT:
3773 ad1884a_hp_automute(codec);
3774 break;
3775 case AD1884A_MIC_EVENT:
3776 ad1884a_hp_automic(codec);
3777 break;
3778 }
Takashi Iwaic5059252008-02-16 09:43:56 +01003779}
3780
3781/* initialize jack-sensing, too */
3782static int ad1884a_hp_init(struct hda_codec *codec)
3783{
3784 ad198x_init(codec);
3785 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02003786 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01003787 return 0;
3788}
3789
3790/* additional verbs for laptop model */
3791static struct hda_verb ad1884a_laptop_verbs[] = {
3792 /* Port-A (HP) pin - always unmuted */
3793 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3794 /* Port-F (int speaker) mixer - route only from analog mixer */
3795 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3796 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3797 /* Port-F pin */
3798 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3799 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Takashi Iwai269ef192008-05-30 15:32:15 +02003800 /* Port-C pin - internal mic-in */
3801 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3802 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3803 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwaic5059252008-02-16 09:43:56 +01003804 /* analog mix */
3805 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3806 /* unsolicited event for pin-sense */
3807 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003808 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaic5059252008-02-16 09:43:56 +01003809 { } /* end */
3810};
3811
3812/*
Takashi Iwaif0813742008-03-18 12:13:03 +01003813 * Thinkpad X300
3814 * 0x11 - HP
3815 * 0x12 - speaker
3816 * 0x14 - mic-in
3817 * 0x17 - built-in mic
3818 */
3819
3820static struct hda_verb ad1984a_thinkpad_verbs[] = {
3821 /* HP unmute */
3822 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3823 /* analog mix */
3824 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3825 /* turn on EAPD */
3826 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
3827 /* unsolicited event for pin-sense */
3828 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
3829 /* internal mic - dmic */
3830 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02003831 /* set magic COEFs for dmic */
3832 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
3833 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01003834 { } /* end */
3835};
3836
3837static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
3838 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3839 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3840 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3841 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3842 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3843 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01003844 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3845 HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
3846 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3847 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3848 {
3849 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3850 .name = "Capture Source",
3851 .info = ad198x_mux_enum_info,
3852 .get = ad198x_mux_enum_get,
3853 .put = ad198x_mux_enum_put,
3854 },
3855 { } /* end */
3856};
3857
3858static struct hda_input_mux ad1984a_thinkpad_capture_source = {
3859 .num_items = 3,
3860 .items = {
3861 { "Mic", 0x0 },
3862 { "Internal Mic", 0x5 },
3863 { "Mix", 0x3 },
3864 },
3865};
3866
3867/* mute internal speaker if HP is plugged */
3868static void ad1984a_thinkpad_automute(struct hda_codec *codec)
3869{
3870 unsigned int present;
3871
3872 present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0)
3873 & AC_PINSENSE_PRESENCE;
3874 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
3875 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3876}
3877
3878/* unsolicited event for HP jack sensing */
3879static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
3880 unsigned int res)
3881{
3882 if ((res >> 26) != AD1884A_HP_EVENT)
3883 return;
3884 ad1984a_thinkpad_automute(codec);
3885}
3886
3887/* initialize jack-sensing, too */
3888static int ad1984a_thinkpad_init(struct hda_codec *codec)
3889{
3890 ad198x_init(codec);
3891 ad1984a_thinkpad_automute(codec);
3892 return 0;
3893}
3894
3895/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003896 */
3897
3898enum {
3899 AD1884A_DESKTOP,
3900 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003901 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01003902 AD1884A_THINKPAD,
Takashi Iwaic5059252008-02-16 09:43:56 +01003903 AD1884A_MODELS
3904};
3905
3906static const char *ad1884a_models[AD1884A_MODELS] = {
3907 [AD1884A_DESKTOP] = "desktop",
3908 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003909 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01003910 [AD1884A_THINKPAD] = "thinkpad",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003911};
3912
3913static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
3914 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01003915 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01003916 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwai67f78572009-02-05 12:14:52 +01003917 SND_PCI_QUIRK(0x103c, 0x3072, "HP", AD1884A_LAPTOP),
Takashi Iwai632da732009-02-05 15:02:06 +01003918 SND_PCI_QUIRK(0x103c, 0x3077, "HP", AD1884A_LAPTOP),
Takashi Iwai11d518e2008-12-10 10:37:33 +01003919 SND_PCI_QUIRK(0x103c, 0x30e6, "HP 6730b", AD1884A_LAPTOP),
Travis Place25424832008-11-10 17:56:23 +01003920 SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP),
Michel Marti65b92e52008-11-08 11:33:32 +01003921 SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP),
Takashi Iwaif0813742008-03-18 12:13:03 +01003922 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003923 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01003924};
3925
3926static int patch_ad1884a(struct hda_codec *codec)
3927{
3928 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003929 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01003930
3931 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3932 if (spec == NULL)
3933 return -ENOMEM;
3934
Takashi Iwaic5059252008-02-16 09:43:56 +01003935 codec->spec = spec;
3936
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003937 err = snd_hda_attach_beep_device(codec, 0x10);
3938 if (err < 0) {
3939 ad198x_free(codec);
3940 return err;
3941 }
3942 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3943
Takashi Iwaic5059252008-02-16 09:43:56 +01003944 spec->multiout.max_channels = 2;
3945 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
3946 spec->multiout.dac_nids = ad1884a_dac_nids;
3947 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
3948 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
3949 spec->adc_nids = ad1884a_adc_nids;
3950 spec->capsrc_nids = ad1884a_capsrc_nids;
3951 spec->input_mux = &ad1884a_capture_source;
3952 spec->num_mixers = 1;
3953 spec->mixers[0] = ad1884a_base_mixers;
3954 spec->num_init_verbs = 1;
3955 spec->init_verbs[0] = ad1884a_init_verbs;
3956 spec->spdif_route = 0;
3957#ifdef CONFIG_SND_HDA_POWER_SAVE
3958 spec->loopback.amplist = ad1884a_loopbacks;
3959#endif
3960 codec->patch_ops = ad198x_patch_ops;
3961
3962 /* override some parameters */
3963 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003964 ad1884a_models,
3965 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01003966 switch (board_config) {
3967 case AD1884A_LAPTOP:
3968 spec->mixers[0] = ad1884a_laptop_mixers;
3969 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
3970 spec->multiout.dig_out_nid = 0;
3971 spec->input_mux = &ad1884a_laptop_capture_source;
3972 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
3973 codec->patch_ops.init = ad1884a_hp_init;
3974 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003975 case AD1884A_MOBILE:
3976 spec->mixers[0] = ad1884a_mobile_mixers;
3977 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
3978 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003979 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
3980 codec->patch_ops.init = ad1884a_hp_init;
3981 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01003982 case AD1884A_THINKPAD:
3983 spec->mixers[0] = ad1984a_thinkpad_mixers;
3984 spec->init_verbs[spec->num_init_verbs++] =
3985 ad1984a_thinkpad_verbs;
3986 spec->multiout.dig_out_nid = 0;
3987 spec->input_mux = &ad1984a_thinkpad_capture_source;
3988 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
3989 codec->patch_ops.init = ad1984a_thinkpad_init;
3990 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01003991 }
3992
3993 return 0;
3994}
3995
3996
3997/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02003998 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02003999 *
4000 * port-A - front hp-out
4001 * port-B - front mic-in
4002 * port-C - rear line-in, shared surr-out (3stack)
4003 * port-D - rear line-out
4004 * port-E - rear mic-in, shared clfe-out (3stack)
4005 * port-F - rear surr-out (6stack)
4006 * port-G - rear clfe-out (6stack)
4007 */
4008
4009static hda_nid_t ad1882_dac_nids[3] = {
4010 0x04, 0x03, 0x05
4011};
4012
4013static hda_nid_t ad1882_adc_nids[2] = {
4014 0x08, 0x09,
4015};
4016
4017static hda_nid_t ad1882_capsrc_nids[2] = {
4018 0x0c, 0x0d,
4019};
4020
4021#define AD1882_SPDIF_OUT 0x02
4022
4023/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
4024static struct hda_input_mux ad1882_capture_source = {
4025 .num_items = 5,
4026 .items = {
4027 { "Front Mic", 0x1 },
4028 { "Mic", 0x4 },
4029 { "Line", 0x2 },
4030 { "CD", 0x3 },
4031 { "Mix", 0x7 },
4032 },
4033};
4034
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004035/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
4036static struct hda_input_mux ad1882a_capture_source = {
4037 .num_items = 5,
4038 .items = {
4039 { "Front Mic", 0x1 },
4040 { "Mic", 0x4},
4041 { "Line", 0x2 },
4042 { "Digital Mic", 0x06 },
4043 { "Mix", 0x7 },
4044 },
4045};
4046
Takashi Iwai0ac85512007-06-20 15:46:13 +02004047static struct snd_kcontrol_new ad1882_base_mixers[] = {
4048 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4049 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4050 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4051 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4052 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4053 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4054 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4055 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004056
Takashi Iwai0ac85512007-06-20 15:46:13 +02004057 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
4058 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
4059 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
4060 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4061 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4062 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4063 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4064 {
4065 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4066 /* The multiple "Capture Source" controls confuse alsamixer
4067 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004068 */
4069 /* .name = "Capture Source", */
4070 .name = "Input Source",
4071 .count = 2,
4072 .info = ad198x_mux_enum_info,
4073 .get = ad198x_mux_enum_get,
4074 .put = ad198x_mux_enum_put,
4075 },
4076 /* SPDIF controls */
4077 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4078 {
4079 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4080 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4081 /* identical with ad1983 */
4082 .info = ad1983_spdif_route_info,
4083 .get = ad1983_spdif_route_get,
4084 .put = ad1983_spdif_route_put,
4085 },
4086 { } /* end */
4087};
4088
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004089static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
4090 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4091 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4092 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4093 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4094 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4095 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4096 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4097 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004098 { } /* end */
4099};
4100
4101static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
4102 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4103 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4104 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4105 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4106 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4107 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4108 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4109 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004110 HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
4111 { } /* end */
4112};
4113
Takashi Iwai0ac85512007-06-20 15:46:13 +02004114static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
4115 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4116 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4117 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4118 {
4119 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4120 .name = "Channel Mode",
4121 .info = ad198x_ch_mode_info,
4122 .get = ad198x_ch_mode_get,
4123 .put = ad198x_ch_mode_put,
4124 },
4125 { } /* end */
4126};
4127
4128static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
4129 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4130 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4131 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4132 { } /* end */
4133};
4134
4135static struct hda_verb ad1882_ch2_init[] = {
4136 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4137 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4138 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4139 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4140 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4141 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4142 { } /* end */
4143};
4144
4145static struct hda_verb ad1882_ch4_init[] = {
4146 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4147 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4148 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4149 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4150 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4151 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4152 { } /* end */
4153};
4154
4155static struct hda_verb ad1882_ch6_init[] = {
4156 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4157 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4158 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4159 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4160 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4161 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4162 { } /* end */
4163};
4164
4165static struct hda_channel_mode ad1882_modes[3] = {
4166 { 2, ad1882_ch2_init },
4167 { 4, ad1882_ch4_init },
4168 { 6, ad1882_ch6_init },
4169};
4170
4171/*
4172 * initialization verbs
4173 */
4174static struct hda_verb ad1882_init_verbs[] = {
4175 /* DACs; mute as default */
4176 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4177 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4178 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4179 /* Port-A (HP) mixer */
4180 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4181 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4182 /* Port-A pin */
4183 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4184 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4185 /* HP selector - select DAC2 */
4186 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4187 /* Port-D (Line-out) mixer */
4188 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4189 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4190 /* Port-D pin */
4191 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4192 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4193 /* Mono-out mixer */
4194 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4195 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4196 /* Mono-out pin */
4197 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4198 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4199 /* Port-B (front mic) pin */
4200 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4201 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4202 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4203 /* Port-C (line-in) pin */
4204 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4205 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4206 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4207 /* Port-C mixer - mute as input */
4208 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4209 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4210 /* Port-E (mic-in) pin */
4211 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4212 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4213 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4214 /* Port-E mixer - mute as input */
4215 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4216 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4217 /* Port-F (surround) */
4218 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4219 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4220 /* Port-G (CLFE) */
4221 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4222 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4223 /* Analog mixer; mute as default */
4224 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4225 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4226 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4227 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4228 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4229 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4230 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4231 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4232 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4233 /* Analog Mix output amp */
4234 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4235 /* SPDIF output selector */
4236 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4237 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4238 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4239 { } /* end */
4240};
4241
Takashi Iwaicb53c622007-08-10 17:21:45 +02004242#ifdef CONFIG_SND_HDA_POWER_SAVE
4243static struct hda_amp_list ad1882_loopbacks[] = {
4244 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4245 { 0x20, HDA_INPUT, 1 }, /* Mic */
4246 { 0x20, HDA_INPUT, 4 }, /* Line */
4247 { 0x20, HDA_INPUT, 6 }, /* CD */
4248 { } /* end */
4249};
4250#endif
4251
Takashi Iwai0ac85512007-06-20 15:46:13 +02004252/* models */
4253enum {
4254 AD1882_3STACK,
4255 AD1882_6STACK,
4256 AD1882_MODELS
4257};
4258
4259static const char *ad1882_models[AD1986A_MODELS] = {
4260 [AD1882_3STACK] = "3stack",
4261 [AD1882_6STACK] = "6stack",
4262};
4263
4264
4265static int patch_ad1882(struct hda_codec *codec)
4266{
4267 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004268 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004269
4270 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4271 if (spec == NULL)
4272 return -ENOMEM;
4273
Takashi Iwai0ac85512007-06-20 15:46:13 +02004274 codec->spec = spec;
4275
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004276 err = snd_hda_attach_beep_device(codec, 0x10);
4277 if (err < 0) {
4278 ad198x_free(codec);
4279 return err;
4280 }
4281 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4282
Takashi Iwai0ac85512007-06-20 15:46:13 +02004283 spec->multiout.max_channels = 6;
4284 spec->multiout.num_dacs = 3;
4285 spec->multiout.dac_nids = ad1882_dac_nids;
4286 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
4287 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
4288 spec->adc_nids = ad1882_adc_nids;
4289 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004290 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004291 spec->input_mux = &ad1882_capture_source;
4292 else
4293 spec->input_mux = &ad1882a_capture_source;
4294 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004295 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004296 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004297 spec->mixers[1] = ad1882_loopback_mixers;
4298 else
4299 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004300 spec->num_init_verbs = 1;
4301 spec->init_verbs[0] = ad1882_init_verbs;
4302 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02004303#ifdef CONFIG_SND_HDA_POWER_SAVE
4304 spec->loopback.amplist = ad1882_loopbacks;
4305#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01004306 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004307
4308 codec->patch_ops = ad198x_patch_ops;
4309
4310 /* override some parameters */
4311 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
4312 ad1882_models, NULL);
4313 switch (board_config) {
4314 default:
4315 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004316 spec->num_mixers = 3;
4317 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004318 spec->channel_mode = ad1882_modes;
4319 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
4320 spec->need_dac_fix = 1;
4321 spec->multiout.max_channels = 2;
4322 spec->multiout.num_dacs = 1;
4323 break;
4324 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004325 spec->num_mixers = 3;
4326 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004327 break;
4328 }
4329 return 0;
4330}
4331
4332
4333/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004334 * patch entries
4335 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004336static struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004337 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02004338 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004339 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004340 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004341 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
4342 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02004343 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
4344 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004345 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004346 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01004347 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02004348 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004349 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02004350 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
4351 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004352 {} /* terminator */
4353};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004354
4355MODULE_ALIAS("snd-hda-codec-id:11d4*");
4356
4357MODULE_LICENSE("GPL");
4358MODULE_DESCRIPTION("Analog Devices HD-audio codec");
4359
4360static struct hda_codec_preset_list analog_list = {
4361 .preset = snd_hda_preset_analog,
4362 .owner = THIS_MODULE,
4363};
4364
4365static int __init patch_analog_init(void)
4366{
4367 return snd_hda_add_codec_preset(&analog_list);
4368}
4369
4370static void __exit patch_analog_exit(void)
4371{
4372 snd_hda_delete_codec_preset(&analog_list);
4373}
4374
4375module_init(patch_analog_init)
4376module_exit(patch_analog_exit)