blob: 266c35e32b64ad65e6984905ba22f9c3ece9e3c5 [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#include <linux/mutex.h>
27
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <sound/core.h>
29#include "hda_codec.h"
30#include "hda_local.h"
31
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;
35
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
Ingo Molnar62932df2006-01-16 16:34:20 +010066 struct mutex amp_mutex; /* PCM volume/mute control mutex */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020067 unsigned int spdif_route;
Takashi Iwaid32410b12005-11-24 16:06:23 +010068
69 /* dynamic controls, init_verbs and input_mux */
70 struct auto_pin_cfg autocfg;
71 unsigned int num_kctl_alloc, num_kctl_used;
72 struct snd_kcontrol_new *kctl_alloc;
73 struct hda_input_mux private_imux;
Takashi Iwai41923e42007-10-22 17:20:10 +020074 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
Takashi Iwaicb53c622007-08-10 17:21:45 +020075
Takashi Iwai8ab78c72007-09-06 14:29:53 +020076 unsigned int jack_present :1;
77
Takashi Iwaicb53c622007-08-10 17:21:45 +020078#ifdef CONFIG_SND_HDA_POWER_SAVE
79 struct hda_loopback_check loopback;
80#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +010081 /* for virtual master */
82 hda_nid_t vmaster_nid;
83 u32 vmaster_tlv[4];
84 const char **slave_vols;
85 const char **slave_sws;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086};
87
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020088/*
89 * input MUX handling (common part)
90 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010091static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020092{
93 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
94 struct ad198x_spec *spec = codec->spec;
95
96 return snd_hda_input_mux_info(spec->input_mux, uinfo);
97}
98
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010099static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200100{
101 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
102 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100103 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200104
Takashi Iwai985be542005-11-02 18:26:49 +0100105 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200106 return 0;
107}
108
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100109static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200110{
111 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
112 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100113 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200114
115 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai2e5b9562005-11-21 16:36:15 +0100116 spec->capsrc_nids[adc_idx],
117 &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200118}
119
120/*
121 * initialization (common callbacks)
122 */
123static int ad198x_init(struct hda_codec *codec)
124{
125 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100126 int i;
127
128 for (i = 0; i < spec->num_init_verbs; i++)
129 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200130 return 0;
131}
132
Takashi Iwai2134ea42008-01-10 16:53:55 +0100133static const char *ad_slave_vols[] = {
134 "Front Playback Volume",
135 "Surround Playback Volume",
136 "Center Playback Volume",
137 "LFE Playback Volume",
138 "Side Playback Volume",
139 "Headphone Playback Volume",
140 "Mono Playback Volume",
Takashi Iwai628ed132008-01-25 11:56:57 +0100141 "Speaker Playback Volume",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100142 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100143 NULL
144};
145
146static const char *ad_slave_sws[] = {
147 "Front Playback Switch",
148 "Surround Playback Switch",
149 "Center Playback Switch",
150 "LFE Playback Switch",
151 "Side Playback Switch",
152 "Headphone Playback Switch",
153 "Mono Playback Switch",
Takashi Iwai628ed132008-01-25 11:56:57 +0100154 "Speaker Playback Switch",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100155 "IEC958 Playback Switch",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100156 NULL
157};
158
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200159static int ad198x_build_controls(struct hda_codec *codec)
160{
161 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100162 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200163 int err;
164
Takashi Iwai985be542005-11-02 18:26:49 +0100165 for (i = 0; i < spec->num_mixers; i++) {
166 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
167 if (err < 0)
168 return err;
169 }
170 if (spec->multiout.dig_out_nid) {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200171 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100172 if (err < 0)
173 return err;
174 }
175 if (spec->dig_in_nid) {
176 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
177 if (err < 0)
178 return err;
179 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100180
181 /* if we have no master control, let's create it */
182 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
183 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
184 HDA_OUTPUT, spec->vmaster_tlv);
185 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
186 spec->vmaster_tlv,
187 (spec->slave_vols ?
188 spec->slave_vols : ad_slave_vols));
189 if (err < 0)
190 return err;
191 }
192 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
193 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
194 NULL,
195 (spec->slave_sws ?
196 spec->slave_sws : ad_slave_sws));
197 if (err < 0)
198 return err;
199 }
200
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200201 return 0;
202}
203
Takashi Iwaicb53c622007-08-10 17:21:45 +0200204#ifdef CONFIG_SND_HDA_POWER_SAVE
205static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
206{
207 struct ad198x_spec *spec = codec->spec;
208 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
209}
210#endif
211
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200212/*
213 * Analog playback callbacks
214 */
215static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
216 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100217 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200218{
219 struct ad198x_spec *spec = codec->spec;
220 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
221}
222
223static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
224 struct hda_codec *codec,
225 unsigned int stream_tag,
226 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100227 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200228{
229 struct ad198x_spec *spec = codec->spec;
230 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
231 format, substream);
232}
233
234static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
235 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100236 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200237{
238 struct ad198x_spec *spec = codec->spec;
239 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
240}
241
242/*
243 * Digital out
244 */
245static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
246 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100247 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200248{
249 struct ad198x_spec *spec = codec->spec;
250 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
251}
252
253static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
254 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100255 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200256{
257 struct ad198x_spec *spec = codec->spec;
258 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
259}
260
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200261static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
262 struct hda_codec *codec,
263 unsigned int stream_tag,
264 unsigned int format,
265 struct snd_pcm_substream *substream)
266{
267 struct ad198x_spec *spec = codec->spec;
268 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
269 format, substream);
270}
271
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200272/*
273 * Analog capture
274 */
275static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
276 struct hda_codec *codec,
277 unsigned int stream_tag,
278 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100279 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200280{
281 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100282 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
283 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200284 return 0;
285}
286
287static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
288 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100289 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200290{
291 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100292 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
293 0, 0, 0);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200294 return 0;
295}
296
297
298/*
299 */
300static struct hda_pcm_stream ad198x_pcm_analog_playback = {
301 .substreams = 1,
302 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100303 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200304 .nid = 0, /* fill later */
305 .ops = {
306 .open = ad198x_playback_pcm_open,
307 .prepare = ad198x_playback_pcm_prepare,
308 .cleanup = ad198x_playback_pcm_cleanup
309 },
310};
311
312static struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100313 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200314 .channels_min = 2,
315 .channels_max = 2,
316 .nid = 0, /* fill later */
317 .ops = {
318 .prepare = ad198x_capture_pcm_prepare,
319 .cleanup = ad198x_capture_pcm_cleanup
320 },
321};
322
323static struct hda_pcm_stream ad198x_pcm_digital_playback = {
324 .substreams = 1,
325 .channels_min = 2,
326 .channels_max = 2,
327 .nid = 0, /* fill later */
328 .ops = {
329 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200330 .close = ad198x_dig_playback_pcm_close,
331 .prepare = ad198x_dig_playback_pcm_prepare
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200332 },
333};
334
Takashi Iwai985be542005-11-02 18:26:49 +0100335static struct hda_pcm_stream ad198x_pcm_digital_capture = {
336 .substreams = 1,
337 .channels_min = 2,
338 .channels_max = 2,
339 /* NID is set in alc_build_pcms */
340};
341
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200342static int ad198x_build_pcms(struct hda_codec *codec)
343{
344 struct ad198x_spec *spec = codec->spec;
345 struct hda_pcm *info = spec->pcm_rec;
346
347 codec->num_pcms = 1;
348 codec->pcm_info = info;
349
350 info->name = "AD198x Analog";
351 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
352 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
353 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
354 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100355 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
356 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200357
358 if (spec->multiout.dig_out_nid) {
359 info++;
360 codec->num_pcms++;
361 info->name = "AD198x Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +0100362 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200363 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
364 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100365 if (spec->dig_in_nid) {
366 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
367 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
368 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200369 }
370
371 return 0;
372}
373
374static void ad198x_free(struct hda_codec *codec)
375{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100376 struct ad198x_spec *spec = codec->spec;
377 unsigned int i;
378
379 if (spec->kctl_alloc) {
380 for (i = 0; i < spec->num_kctl_used; i++)
381 kfree(spec->kctl_alloc[i].name);
382 kfree(spec->kctl_alloc);
383 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200384 kfree(codec->spec);
385}
386
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200387static struct hda_codec_ops ad198x_patch_ops = {
388 .build_controls = ad198x_build_controls,
389 .build_pcms = ad198x_build_pcms,
390 .init = ad198x_init,
391 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200392#ifdef CONFIG_SND_HDA_POWER_SAVE
393 .check_power_status = ad198x_check_power_status,
394#endif
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200395};
396
397
398/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100399 * EAPD control
400 * the private value = nid | (invert << 8)
401 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200402#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100403
404static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
405 struct snd_ctl_elem_value *ucontrol)
406{
407 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
408 struct ad198x_spec *spec = codec->spec;
409 int invert = (kcontrol->private_value >> 8) & 1;
410 if (invert)
411 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
412 else
413 ucontrol->value.integer.value[0] = spec->cur_eapd;
414 return 0;
415}
416
417static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
418 struct snd_ctl_elem_value *ucontrol)
419{
420 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
421 struct ad198x_spec *spec = codec->spec;
422 int invert = (kcontrol->private_value >> 8) & 1;
423 hda_nid_t nid = kcontrol->private_value & 0xff;
424 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100425 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwai18a815d2006-03-01 19:54:39 +0100426 if (invert)
427 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200428 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100429 return 0;
430 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200431 snd_hda_codec_write_cache(codec, nid,
432 0, AC_VERB_SET_EAPD_BTLENABLE,
433 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100434 return 1;
435}
436
Takashi Iwai9230d212006-03-13 13:49:49 +0100437static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
438 struct snd_ctl_elem_info *uinfo);
439static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
440 struct snd_ctl_elem_value *ucontrol);
441static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
442 struct snd_ctl_elem_value *ucontrol);
443
444
Takashi Iwai18a815d2006-03-01 19:54:39 +0100445/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200446 * AD1986A specific
447 */
448
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449#define AD1986A_SPDIF_OUT 0x02
450#define AD1986A_FRONT_DAC 0x03
451#define AD1986A_SURR_DAC 0x04
452#define AD1986A_CLFE_DAC 0x05
453#define AD1986A_ADC 0x06
454
455static hda_nid_t ad1986a_dac_nids[3] = {
456 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
457};
Takashi Iwai985be542005-11-02 18:26:49 +0100458static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +0100459static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460
461static struct hda_input_mux ad1986a_capture_source = {
462 .num_items = 7,
463 .items = {
464 { "Mic", 0x0 },
465 { "CD", 0x1 },
466 { "Aux", 0x3 },
467 { "Line", 0x4 },
468 { "Mix", 0x5 },
469 { "Mono", 0x6 },
470 { "Phone", 0x7 },
471 },
472};
473
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474
Takashi Iwai532d5382007-07-27 19:02:40 +0200475static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
476 .ops = &snd_hda_bind_vol,
477 .values = {
478 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
479 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
480 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
481 0
482 },
483};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484
Takashi Iwai532d5382007-07-27 19:02:40 +0200485static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
486 .ops = &snd_hda_bind_sw,
487 .values = {
488 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
489 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
490 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
491 0
492 },
493};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494
495/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 * mixers
497 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100498static struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200499 /*
500 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
501 */
502 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
503 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
505 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
506 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
507 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
508 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
509 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
510 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
511 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
512 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
513 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
514 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
515 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
516 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
517 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
518 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
519 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
520 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
521 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100522 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
524 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
525 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
526 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
527 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
528 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
529 {
530 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
531 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200532 .info = ad198x_mux_enum_info,
533 .get = ad198x_mux_enum_get,
534 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 },
536 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
537 { } /* end */
538};
539
Takashi Iwai9230d212006-03-13 13:49:49 +0100540/* additional mixers for 3stack mode */
541static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
542 {
543 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
544 .name = "Channel Mode",
545 .info = ad198x_ch_mode_info,
546 .get = ad198x_ch_mode_get,
547 .put = ad198x_ch_mode_put,
548 },
549 { } /* end */
550};
551
552/* laptop model - 2ch only */
553static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
554
Takashi Iwai20a45e82007-08-15 22:20:45 +0200555/* master controls both pins 0x1a and 0x1b */
556static struct hda_bind_ctls ad1986a_laptop_master_vol = {
557 .ops = &snd_hda_bind_vol,
558 .values = {
559 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
560 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
561 0,
562 },
563};
564
565static struct hda_bind_ctls ad1986a_laptop_master_sw = {
566 .ops = &snd_hda_bind_sw,
567 .values = {
568 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
569 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
570 0,
571 },
572};
573
Takashi Iwai9230d212006-03-13 13:49:49 +0100574static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
575 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
576 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200577 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
578 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100579 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
580 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
581 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
582 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
583 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
584 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
585 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
586 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100587 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai9230d212006-03-13 13:49:49 +0100588 /* HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
589 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
590 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
591 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
592 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
593 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
594 {
595 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
596 .name = "Capture Source",
597 .info = ad198x_mux_enum_info,
598 .get = ad198x_mux_enum_get,
599 .put = ad198x_mux_enum_put,
600 },
601 { } /* end */
602};
603
Takashi Iwai825aa9722006-03-17 10:50:49 +0100604/* laptop-eapd model - 2ch only */
605
Takashi Iwai825aa9722006-03-17 10:50:49 +0100606static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
607 .num_items = 3,
608 .items = {
609 { "Mic", 0x0 },
610 { "Internal Mic", 0x4 },
611 { "Mix", 0x5 },
612 },
613};
614
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100615static struct hda_input_mux ad1986a_automic_capture_source = {
616 .num_items = 2,
617 .items = {
618 { "Mic", 0x0 },
619 { "Mix", 0x5 },
620 },
621};
622
Takashi Iwai825aa9722006-03-17 10:50:49 +0100623static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200624 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
625 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai825aa9722006-03-17 10:50:49 +0100626 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
627 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai825aa9722006-03-17 10:50:49 +0100628 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
629 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwaife8970b2007-02-26 16:00:34 +0100630 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai825aa9722006-03-17 10:50:49 +0100631 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
632 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
633 {
634 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
635 .name = "Capture Source",
636 .info = ad198x_mux_enum_info,
637 .get = ad198x_mux_enum_get,
638 .put = ad198x_mux_enum_put,
639 },
640 {
641 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
642 .name = "External Amplifier",
643 .info = ad198x_eapd_info,
644 .get = ad198x_eapd_get,
645 .put = ad198x_eapd_put,
646 .private_value = 0x1b | (1 << 8), /* port-D, inversed */
647 },
648 { } /* end */
649};
650
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100651/* re-connect the mic boost input according to the jack sensing */
652static void ad1986a_automic(struct hda_codec *codec)
653{
654 unsigned int present;
655 present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0);
656 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
657 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
658 (present & AC_PINSENSE_PRESENCE) ? 0 : 2);
659}
660
661#define AD1986A_MIC_EVENT 0x36
662
663static void ad1986a_automic_unsol_event(struct hda_codec *codec,
664 unsigned int res)
665{
666 if ((res >> 26) != AD1986A_MIC_EVENT)
667 return;
668 ad1986a_automic(codec);
669}
670
671static int ad1986a_automic_init(struct hda_codec *codec)
672{
673 ad198x_init(codec);
674 ad1986a_automic(codec);
675 return 0;
676}
677
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200678/* laptop-automute - 2ch only */
679
680static void ad1986a_update_hp(struct hda_codec *codec)
681{
682 struct ad198x_spec *spec = codec->spec;
683 unsigned int mute;
684
685 if (spec->jack_present)
686 mute = HDA_AMP_MUTE; /* mute internal speaker */
687 else
688 /* unmute internal speaker if necessary */
689 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
690 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
691 HDA_AMP_MUTE, mute);
692}
693
694static void ad1986a_hp_automute(struct hda_codec *codec)
695{
696 struct ad198x_spec *spec = codec->spec;
697 unsigned int present;
698
699 present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0);
Takashi Iwai53eb1b82007-10-17 10:09:32 +0200700 /* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
701 spec->jack_present = !(present & 0x80000000);
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200702 ad1986a_update_hp(codec);
703}
704
705#define AD1986A_HP_EVENT 0x37
706
707static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
708{
709 if ((res >> 26) != AD1986A_HP_EVENT)
710 return;
711 ad1986a_hp_automute(codec);
712}
713
714static int ad1986a_hp_init(struct hda_codec *codec)
715{
716 ad198x_init(codec);
717 ad1986a_hp_automute(codec);
718 return 0;
719}
720
721/* bind hp and internal speaker mute (with plug check) */
722static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
723 struct snd_ctl_elem_value *ucontrol)
724{
725 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
726 long *valp = ucontrol->value.integer.value;
727 int change;
728
729 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
730 HDA_AMP_MUTE,
731 valp[0] ? 0 : HDA_AMP_MUTE);
732 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
733 HDA_AMP_MUTE,
734 valp[1] ? 0 : HDA_AMP_MUTE);
735 if (change)
736 ad1986a_update_hp(codec);
737 return change;
738}
739
740static struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = {
741 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
742 {
743 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
744 .name = "Master Playback Switch",
745 .info = snd_hda_mixer_amp_switch_info,
746 .get = snd_hda_mixer_amp_switch_get,
747 .put = ad1986a_hp_master_sw_put,
748 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
749 },
750 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
751 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
752 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT),
753 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT),
754 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
755 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
756 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
757 HDA_CODEC_VOLUME("Beep Playback Volume", 0x18, 0x0, HDA_OUTPUT),
758 HDA_CODEC_MUTE("Beep Playback Switch", 0x18, 0x0, HDA_OUTPUT),
759 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
760 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
761 {
762 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
763 .name = "Capture Source",
764 .info = ad198x_mux_enum_info,
765 .get = ad198x_mux_enum_get,
766 .put = ad198x_mux_enum_put,
767 },
768 {
769 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
770 .name = "External Amplifier",
771 .info = ad198x_eapd_info,
772 .get = ad198x_eapd_get,
773 .put = ad198x_eapd_put,
774 .private_value = 0x1b | (1 << 8), /* port-D, inversed */
775 },
776 { } /* end */
777};
778
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779/*
780 * initialization verbs
781 */
782static struct hda_verb ad1986a_init_verbs[] = {
783 /* Front, Surround, CLFE DAC; mute as default */
784 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
785 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
786 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
787 /* Downmix - off */
788 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
789 /* HP, Line-Out, Surround, CLFE selectors */
790 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
791 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
792 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
793 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
794 /* Mono selector */
795 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
796 /* Mic selector: Mic 1/2 pin */
797 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
798 /* Line-in selector: Line-in */
799 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
800 /* Mic 1/2 swap */
801 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
802 /* Record selector: mic */
803 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
804 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
805 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
806 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
807 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
808 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
809 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
810 /* PC beep */
811 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
812 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
813 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
814 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
815 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
816 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
817 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200818 /* HP Pin */
819 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
820 /* Front, Surround, CLFE Pins */
821 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
822 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
823 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
824 /* Mono Pin */
825 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
826 /* Mic Pin */
827 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
828 /* Line, Aux, CD, Beep-In Pin */
829 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
830 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
831 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
832 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
833 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 { } /* end */
835};
836
Takashi Iwai9230d212006-03-13 13:49:49 +0100837static struct hda_verb ad1986a_ch2_init[] = {
838 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200839 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
840 /* Line-in selectors */
841 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100842 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200843 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
844 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
845 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100846 { } /* end */
847};
848
849static struct hda_verb ad1986a_ch4_init[] = {
850 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200851 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
852 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100853 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200854 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
855 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100856 { } /* end */
857};
858
859static struct hda_verb ad1986a_ch6_init[] = {
860 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200861 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
862 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100863 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +0200864 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
865 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +0100866 { } /* end */
867};
868
869static struct hda_channel_mode ad1986a_modes[3] = {
870 { 2, ad1986a_ch2_init },
871 { 4, ad1986a_ch4_init },
872 { 6, ad1986a_ch6_init },
873};
874
Takashi Iwai825aa9722006-03-17 10:50:49 +0100875/* eapd initialization */
876static struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +0100877 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa9722006-03-17 10:50:49 +0100878 {}
879};
880
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100881static struct hda_verb ad1986a_automic_verbs[] = {
882 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
883 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
884 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
885 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
886 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
887 {}
888};
889
Tobin Davisf36090f2007-01-08 11:07:12 +0100890/* Ultra initialization */
891static struct hda_verb ad1986a_ultra_init[] = {
892 /* eapd initialization */
893 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
894 /* CLFE -> Mic in */
895 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
896 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
897 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
898 { } /* end */
899};
900
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200901/* pin sensing on HP jack */
902static struct hda_verb ad1986a_hp_init_verbs[] = {
903 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
904 {}
905};
906
907
Takashi Iwai9230d212006-03-13 13:49:49 +0100908/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100909enum {
910 AD1986A_6STACK,
911 AD1986A_3STACK,
912 AD1986A_LAPTOP,
913 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200914 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +0100915 AD1986A_ULTRA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100916 AD1986A_MODELS
917};
Takashi Iwai9230d212006-03-13 13:49:49 +0100918
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100919static const char *ad1986a_models[AD1986A_MODELS] = {
920 [AD1986A_6STACK] = "6stack",
921 [AD1986A_3STACK] = "3stack",
922 [AD1986A_LAPTOP] = "laptop",
923 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200924 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +0100925 [AD1986A_ULTRA] = "ultra",
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100926};
927
928static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
929 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100930 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100931 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +0100932 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100933 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
934 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
935 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
936 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +0100937 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +0200938 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100939 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
940 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
941 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
942 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
943 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +0100944 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Takashi Iwai7db756f2007-12-24 14:36:09 +0100945 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
Tobin Davis18768992007-03-12 22:20:51 +0100946 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100947 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
948 SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),
949 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),
950 SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),
Tobin Davisf36090f2007-01-08 11:07:12 +0100951 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaiac3e3742007-12-17 17:14:18 +0100952 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +0100953 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100954 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200955 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +0100956 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +0100957 {}
958};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959
Takashi Iwaicb53c622007-08-10 17:21:45 +0200960#ifdef CONFIG_SND_HDA_POWER_SAVE
961static struct hda_amp_list ad1986a_loopbacks[] = {
962 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
963 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
964 { 0x15, HDA_OUTPUT, 0 }, /* CD */
965 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
966 { 0x17, HDA_OUTPUT, 0 }, /* Line */
967 { } /* end */
968};
969#endif
970
Takashi Iwai8c0d9642008-01-28 12:30:17 +0100971static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
972{
973 unsigned int conf = snd_hda_codec_read(codec, nid, 0,
974 AC_VERB_GET_CONFIG_DEFAULT, 0);
975 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
976}
977
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978static int patch_ad1986a(struct hda_codec *codec)
979{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200980 struct ad198x_spec *spec;
Takashi Iwai9230d212006-03-13 13:49:49 +0100981 int board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982
Takashi Iwaie560d8d2005-09-09 14:21:46 +0200983 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 if (spec == NULL)
985 return -ENOMEM;
986
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 codec->spec = spec;
988
989 spec->multiout.max_channels = 6;
990 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
991 spec->multiout.dac_nids = ad1986a_dac_nids;
992 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +0100993 spec->num_adc_nids = 1;
994 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +0100995 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200996 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +0100997 spec->num_mixers = 1;
998 spec->mixers[0] = ad1986a_mixers;
999 spec->num_init_verbs = 1;
1000 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001001#ifdef CONFIG_SND_HDA_POWER_SAVE
1002 spec->loopback.amplist = ad1986a_loopbacks;
1003#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001004 spec->vmaster_nid = 0x1b;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001006 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007
Takashi Iwai9230d212006-03-13 13:49:49 +01001008 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001009 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1010 ad1986a_models,
1011 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001012 switch (board_config) {
1013 case AD1986A_3STACK:
1014 spec->num_mixers = 2;
1015 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001016 spec->num_init_verbs = 2;
1017 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001018 spec->channel_mode = ad1986a_modes;
1019 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001020 spec->need_dac_fix = 1;
1021 spec->multiout.max_channels = 2;
1022 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001023 break;
1024 case AD1986A_LAPTOP:
1025 spec->mixers[0] = ad1986a_laptop_mixers;
1026 spec->multiout.max_channels = 2;
1027 spec->multiout.num_dacs = 1;
1028 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1029 break;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001030 case AD1986A_LAPTOP_EAPD:
1031 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001032 spec->num_init_verbs = 3;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001033 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001034 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001035 spec->multiout.max_channels = 2;
1036 spec->multiout.num_dacs = 1;
1037 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001038 if (!is_jack_available(codec, 0x25))
1039 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001040 spec->input_mux = &ad1986a_automic_capture_source;
1041 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1042 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001043 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001044 case AD1986A_LAPTOP_AUTOMUTE:
1045 spec->mixers[0] = ad1986a_laptop_automute_mixers;
1046 spec->num_init_verbs = 3;
1047 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1048 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1049 spec->multiout.max_channels = 2;
1050 spec->multiout.num_dacs = 1;
1051 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001052 if (!is_jack_available(codec, 0x25))
1053 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001054 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1055 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1056 codec->patch_ops.init = ad1986a_hp_init;
1057 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001058 case AD1986A_ULTRA:
1059 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1060 spec->num_init_verbs = 2;
1061 spec->init_verbs[1] = ad1986a_ultra_init;
1062 spec->multiout.max_channels = 2;
1063 spec->multiout.num_dacs = 1;
1064 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1065 spec->multiout.dig_out_nid = 0;
1066 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001067 }
1068
Takashi Iwaid29240c2007-10-26 12:35:56 +02001069 /* AD1986A has a hardware problem that it can't share a stream
1070 * with multiple output pins. The copy of front to surrounds
1071 * causes noisy or silent outputs at a certain timing, e.g.
1072 * changing the volume.
1073 * So, let's disable the shared stream.
1074 */
1075 spec->multiout.no_share_stream = 1;
1076
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 return 0;
1078}
1079
1080/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001081 * AD1983 specific
1082 */
1083
1084#define AD1983_SPDIF_OUT 0x02
1085#define AD1983_DAC 0x03
1086#define AD1983_ADC 0x04
1087
1088static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001089static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001090static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001091
1092static struct hda_input_mux ad1983_capture_source = {
1093 .num_items = 4,
1094 .items = {
1095 { "Mic", 0x0 },
1096 { "Line", 0x1 },
1097 { "Mix", 0x2 },
1098 { "Mix Mono", 0x3 },
1099 },
1100};
1101
1102/*
1103 * SPDIF playback route
1104 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001105static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001106{
1107 static char *texts[] = { "PCM", "ADC" };
1108
1109 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1110 uinfo->count = 1;
1111 uinfo->value.enumerated.items = 2;
1112 if (uinfo->value.enumerated.item > 1)
1113 uinfo->value.enumerated.item = 1;
1114 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1115 return 0;
1116}
1117
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001118static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001119{
1120 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1121 struct ad198x_spec *spec = codec->spec;
1122
1123 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1124 return 0;
1125}
1126
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001127static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001128{
1129 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1130 struct ad198x_spec *spec = codec->spec;
1131
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001132 if (ucontrol->value.enumerated.item[0] > 1)
1133 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001134 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1135 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001136 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1137 AC_VERB_SET_CONNECT_SEL,
1138 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001139 return 1;
1140 }
1141 return 0;
1142}
1143
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001144static struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001145 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1146 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1147 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1148 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1149 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1150 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1151 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1152 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1153 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1154 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1155 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1156 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1157 HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x10, 1, 0x0, HDA_OUTPUT),
1158 HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x10, 1, 0x0, HDA_OUTPUT),
1159 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
1160 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1161 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1162 {
1163 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1164 .name = "Capture Source",
1165 .info = ad198x_mux_enum_info,
1166 .get = ad198x_mux_enum_get,
1167 .put = ad198x_mux_enum_put,
1168 },
1169 {
1170 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001171 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001172 .info = ad1983_spdif_route_info,
1173 .get = ad1983_spdif_route_get,
1174 .put = ad1983_spdif_route_put,
1175 },
1176 { } /* end */
1177};
1178
1179static struct hda_verb ad1983_init_verbs[] = {
1180 /* Front, HP, Mono; mute as default */
1181 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1182 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1183 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1184 /* Beep, PCM, Mic, Line-In: mute */
1185 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1186 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1187 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1188 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1189 /* Front, HP selectors; from Mix */
1190 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1191 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1192 /* Mono selector; from Mix */
1193 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1194 /* Mic selector; Mic */
1195 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1196 /* Line-in selector: Line-in */
1197 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1198 /* Mic boost: 0dB */
1199 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1200 /* Record selector: mic */
1201 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1202 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1203 /* SPDIF route: PCM */
1204 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1205 /* Front Pin */
1206 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1207 /* HP Pin */
1208 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1209 /* Mono Pin */
1210 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1211 /* Mic Pin */
1212 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1213 /* Line Pin */
1214 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1215 { } /* end */
1216};
1217
Takashi Iwaicb53c622007-08-10 17:21:45 +02001218#ifdef CONFIG_SND_HDA_POWER_SAVE
1219static struct hda_amp_list ad1983_loopbacks[] = {
1220 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1221 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1222 { } /* end */
1223};
1224#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001225
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001226static int patch_ad1983(struct hda_codec *codec)
1227{
1228 struct ad198x_spec *spec;
1229
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001230 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001231 if (spec == NULL)
1232 return -ENOMEM;
1233
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001234 codec->spec = spec;
1235
1236 spec->multiout.max_channels = 2;
1237 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1238 spec->multiout.dac_nids = ad1983_dac_nids;
1239 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001240 spec->num_adc_nids = 1;
1241 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001242 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001243 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001244 spec->num_mixers = 1;
1245 spec->mixers[0] = ad1983_mixers;
1246 spec->num_init_verbs = 1;
1247 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001248 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001249#ifdef CONFIG_SND_HDA_POWER_SAVE
1250 spec->loopback.amplist = ad1983_loopbacks;
1251#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001252 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001253
1254 codec->patch_ops = ad198x_patch_ops;
1255
1256 return 0;
1257}
1258
1259
1260/*
1261 * AD1981 HD specific
1262 */
1263
1264#define AD1981_SPDIF_OUT 0x02
1265#define AD1981_DAC 0x03
1266#define AD1981_ADC 0x04
1267
1268static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +01001269static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
Takashi Iwai18a815d2006-03-01 19:54:39 +01001270static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001271
1272/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
1273static struct hda_input_mux ad1981_capture_source = {
1274 .num_items = 7,
1275 .items = {
1276 { "Front Mic", 0x0 },
1277 { "Line", 0x1 },
1278 { "Mix", 0x2 },
1279 { "Mix Mono", 0x3 },
1280 { "CD", 0x4 },
1281 { "Mic", 0x6 },
1282 { "Aux", 0x7 },
1283 },
1284};
1285
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001286static struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001287 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1288 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1289 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1290 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1291 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1292 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1293 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1294 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1295 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1296 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1297 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1298 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1299 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1300 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1301 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1302 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1303 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1304 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1305 HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
1306 HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x0d, 1, 0x0, HDA_OUTPUT),
1307 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
1308 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
1309 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1310 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1311 {
1312 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1313 .name = "Capture Source",
1314 .info = ad198x_mux_enum_info,
1315 .get = ad198x_mux_enum_get,
1316 .put = ad198x_mux_enum_put,
1317 },
1318 /* identical with AD1983 */
1319 {
1320 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001321 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001322 .info = ad1983_spdif_route_info,
1323 .get = ad1983_spdif_route_get,
1324 .put = ad1983_spdif_route_put,
1325 },
1326 { } /* end */
1327};
1328
1329static struct hda_verb ad1981_init_verbs[] = {
1330 /* Front, HP, Mono; mute as default */
1331 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1332 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1333 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1334 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1335 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1336 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1337 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1338 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1339 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1340 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1341 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1342 /* Front, HP selectors; from Mix */
1343 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1344 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1345 /* Mono selector; from Mix */
1346 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1347 /* Mic Mixer; select Front Mic */
1348 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1349 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1350 /* Mic boost: 0dB */
1351 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1352 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1353 /* Record selector: Front mic */
1354 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1355 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1356 /* SPDIF route: PCM */
1357 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1358 /* Front Pin */
1359 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1360 /* HP Pin */
1361 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1362 /* Mono Pin */
1363 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1364 /* Front & Rear Mic Pins */
1365 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1366 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1367 /* Line Pin */
1368 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1369 /* Digital Beep */
1370 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1371 /* Line-Out as Input: disabled */
1372 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1373 { } /* end */
1374};
1375
Takashi Iwaicb53c622007-08-10 17:21:45 +02001376#ifdef CONFIG_SND_HDA_POWER_SAVE
1377static struct hda_amp_list ad1981_loopbacks[] = {
1378 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1379 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1380 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1381 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1382 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1383 { } /* end */
1384};
1385#endif
1386
Takashi Iwai18a815d2006-03-01 19:54:39 +01001387/*
1388 * Patch for HP nx6320
1389 *
Tobin Davis18768992007-03-12 22:20:51 +01001390 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001391 * speaker output enabled _and_ mute-LED off.
1392 */
1393
1394#define AD1981_HP_EVENT 0x37
1395#define AD1981_MIC_EVENT 0x38
1396
1397static struct hda_verb ad1981_hp_init_verbs[] = {
1398 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1399 /* pin sensing on HP and Mic jacks */
1400 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1401 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1402 {}
1403};
1404
1405/* turn on/off EAPD (+ mute HP) as a master switch */
1406static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1407 struct snd_ctl_elem_value *ucontrol)
1408{
1409 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1410 struct ad198x_spec *spec = codec->spec;
1411
1412 if (! ad198x_eapd_put(kcontrol, ucontrol))
1413 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001414 /* change speaker pin appropriately */
1415 snd_hda_codec_write(codec, 0x05, 0,
1416 AC_VERB_SET_PIN_WIDGET_CONTROL,
1417 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001418 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001419 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1420 HDA_AMP_MUTE,
1421 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001422 return 1;
1423}
1424
1425/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwaicca3b372007-08-10 17:12:15 +02001426static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
1427 .ops = &snd_hda_bind_vol,
1428 .values = {
1429 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1430 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1431 0
1432 },
1433};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001434
1435/* mute internal speaker if HP is plugged */
1436static void ad1981_hp_automute(struct hda_codec *codec)
1437{
1438 unsigned int present;
1439
1440 present = snd_hda_codec_read(codec, 0x06, 0,
1441 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
Takashi Iwai47fd8302007-08-10 17:11:07 +02001442 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1443 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001444}
1445
1446/* toggle input of built-in and mic jack appropriately */
1447static void ad1981_hp_automic(struct hda_codec *codec)
1448{
1449 static struct hda_verb mic_jack_on[] = {
1450 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1451 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1452 {}
1453 };
1454 static struct hda_verb mic_jack_off[] = {
1455 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1456 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1457 {}
1458 };
1459 unsigned int present;
1460
1461 present = snd_hda_codec_read(codec, 0x08, 0,
1462 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1463 if (present)
1464 snd_hda_sequence_write(codec, mic_jack_on);
1465 else
1466 snd_hda_sequence_write(codec, mic_jack_off);
1467}
1468
1469/* unsolicited event for HP jack sensing */
1470static void ad1981_hp_unsol_event(struct hda_codec *codec,
1471 unsigned int res)
1472{
1473 res >>= 26;
1474 switch (res) {
1475 case AD1981_HP_EVENT:
1476 ad1981_hp_automute(codec);
1477 break;
1478 case AD1981_MIC_EVENT:
1479 ad1981_hp_automic(codec);
1480 break;
1481 }
1482}
1483
1484static struct hda_input_mux ad1981_hp_capture_source = {
1485 .num_items = 3,
1486 .items = {
1487 { "Mic", 0x0 },
1488 { "Docking-Station", 0x1 },
1489 { "Mix", 0x2 },
1490 },
1491};
1492
1493static struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001494 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001495 {
1496 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1497 .name = "Master Playback Switch",
1498 .info = ad198x_eapd_info,
1499 .get = ad198x_eapd_get,
1500 .put = ad1981_hp_master_sw_put,
1501 .private_value = 0x05,
1502 },
1503 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1504 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1505#if 0
1506 /* FIXME: analog mic/line loopback doesn't work with my tests...
1507 * (although recording is OK)
1508 */
1509 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1510 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1511 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1512 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1513 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1514 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1515 /* FIXME: does this laptop have analog CD connection? */
1516 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1517 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1518#endif
1519 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1520 HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
1521 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1522 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1523 {
1524 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1525 .name = "Capture Source",
1526 .info = ad198x_mux_enum_info,
1527 .get = ad198x_mux_enum_get,
1528 .put = ad198x_mux_enum_put,
1529 },
1530 { } /* end */
1531};
1532
1533/* initialize jack-sensing, too */
1534static int ad1981_hp_init(struct hda_codec *codec)
1535{
1536 ad198x_init(codec);
1537 ad1981_hp_automute(codec);
1538 ad1981_hp_automic(codec);
1539 return 0;
1540}
1541
Tobin Davis18768992007-03-12 22:20:51 +01001542/* configuration for Toshiba Laptops */
1543static struct hda_verb ad1981_toshiba_init_verbs[] = {
1544 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1545 /* pin sensing on HP and Mic jacks */
1546 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1547 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1548 {}
1549};
1550
1551static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
1552 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1553 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1554 { }
1555};
1556
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001557/* configuration for Lenovo Thinkpad T60 */
1558static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
1559 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1560 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1561 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1562 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1563 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1564 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1565 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1566 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1567 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
1568 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1569 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1570 {
1571 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1572 .name = "Capture Source",
1573 .info = ad198x_mux_enum_info,
1574 .get = ad198x_mux_enum_get,
1575 .put = ad198x_mux_enum_put,
1576 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001577 /* identical with AD1983 */
1578 {
1579 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1580 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1581 .info = ad1983_spdif_route_info,
1582 .get = ad1983_spdif_route_get,
1583 .put = ad1983_spdif_route_put,
1584 },
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001585 { } /* end */
1586};
1587
1588static struct hda_input_mux ad1981_thinkpad_capture_source = {
1589 .num_items = 3,
1590 .items = {
1591 { "Mic", 0x0 },
1592 { "Mix", 0x2 },
1593 { "CD", 0x4 },
1594 },
1595};
1596
Takashi Iwai18a815d2006-03-01 19:54:39 +01001597/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001598enum {
1599 AD1981_BASIC,
1600 AD1981_HP,
1601 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001602 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001603 AD1981_MODELS
1604};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001605
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001606static const char *ad1981_models[AD1981_MODELS] = {
1607 [AD1981_HP] = "hp",
1608 [AD1981_THINKPAD] = "thinkpad",
1609 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001610 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001611};
1612
1613static struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001614 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001615 /* All HP models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001616 SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001617 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001618 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001619 SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001620 /* HP nx6320 (reversed SSID, H/W bug) */
1621 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001622 {}
1623};
1624
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001625static int patch_ad1981(struct hda_codec *codec)
1626{
1627 struct ad198x_spec *spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001628 int board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001629
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001630 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001631 if (spec == NULL)
1632 return -ENOMEM;
1633
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001634 codec->spec = spec;
1635
1636 spec->multiout.max_channels = 2;
1637 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1638 spec->multiout.dac_nids = ad1981_dac_nids;
1639 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001640 spec->num_adc_nids = 1;
1641 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001642 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001643 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001644 spec->num_mixers = 1;
1645 spec->mixers[0] = ad1981_mixers;
1646 spec->num_init_verbs = 1;
1647 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001648 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001649#ifdef CONFIG_SND_HDA_POWER_SAVE
1650 spec->loopback.amplist = ad1981_loopbacks;
1651#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001652 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001653
1654 codec->patch_ops = ad198x_patch_ops;
1655
Takashi Iwai18a815d2006-03-01 19:54:39 +01001656 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001657 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1658 ad1981_models,
1659 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001660 switch (board_config) {
1661 case AD1981_HP:
1662 spec->mixers[0] = ad1981_hp_mixers;
1663 spec->num_init_verbs = 2;
1664 spec->init_verbs[1] = ad1981_hp_init_verbs;
1665 spec->multiout.dig_out_nid = 0;
1666 spec->input_mux = &ad1981_hp_capture_source;
1667
1668 codec->patch_ops.init = ad1981_hp_init;
1669 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1670 break;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001671 case AD1981_THINKPAD:
1672 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001673 spec->input_mux = &ad1981_thinkpad_capture_source;
1674 break;
Tobin Davis18768992007-03-12 22:20:51 +01001675 case AD1981_TOSHIBA:
1676 spec->mixers[0] = ad1981_hp_mixers;
1677 spec->mixers[1] = ad1981_toshiba_mixers;
1678 spec->num_init_verbs = 2;
1679 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1680 spec->multiout.dig_out_nid = 0;
1681 spec->input_mux = &ad1981_hp_capture_source;
1682 codec->patch_ops.init = ad1981_hp_init;
1683 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1684 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001685 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001686 return 0;
1687}
1688
1689
1690/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001691 * AD1988
1692 *
1693 * Output pins and routes
1694 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001695 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001696 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1697 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1698 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1699 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1700 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1701 * port-F 0x16 (mute) <- 0x2a <- 06
1702 * port-G 0x24 (mute) <- 0x27 <- 05
1703 * port-H 0x25 (mute) <- 0x28 <- 0a
1704 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1705 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001706 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1707 * (*) 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 +01001708 *
1709 * Input pins and routes
1710 *
1711 * pin boost mix input # / adc input #
1712 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1713 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1714 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1715 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1716 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1717 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1718 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1719 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1720 *
1721 *
1722 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001723 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001724 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001725 *
1726 * Inputs of Analog Mix (0x20)
1727 * 0:Port-B (front mic)
1728 * 1:Port-C/G/H (line-in)
1729 * 2:Port-A
1730 * 3:Port-D (line-in/2)
1731 * 4:Port-E/G/H (mic-in)
1732 * 5:Port-F (mic2-in)
1733 * 6:CD
1734 * 7:Beep
1735 *
1736 * ADC selection
1737 * 0:Port-A
1738 * 1:Port-B (front mic-in)
1739 * 2:Port-C (line-in)
1740 * 3:Port-F (mic2-in)
1741 * 4:Port-E (mic-in)
1742 * 5:CD
1743 * 6:Port-G
1744 * 7:Port-H
1745 * 8:Port-D (line-in/2)
1746 * 9:Mix
1747 *
1748 * Proposed pin assignments by the datasheet
1749 *
1750 * 6-stack
1751 * Port-A front headphone
1752 * B front mic-in
1753 * C rear line-in
1754 * D rear front-out
1755 * E rear mic-in
1756 * F rear surround
1757 * G rear CLFE
1758 * H rear side
1759 *
1760 * 3-stack
1761 * Port-A front headphone
1762 * B front mic
1763 * C rear line-in/surround
1764 * D rear front-out
1765 * E rear mic-in/CLFE
1766 *
1767 * laptop
1768 * Port-A headphone
1769 * B mic-in
1770 * C docking station
1771 * D internal speaker (with EAPD)
1772 * E/F quad mic array
1773 */
1774
1775
1776/* models */
1777enum {
1778 AD1988_6STACK,
1779 AD1988_6STACK_DIG,
1780 AD1988_3STACK,
1781 AD1988_3STACK_DIG,
1782 AD1988_LAPTOP,
1783 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01001784 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001785 AD1988_MODEL_LAST,
1786};
1787
Takashi Iwaid32410b12005-11-24 16:06:23 +01001788/* reivision id to check workarounds */
1789#define AD1988A_REV2 0x100200
1790
Takashi Iwai1a806f42006-07-03 15:58:16 +02001791#define is_rev2(codec) \
1792 ((codec)->vendor_id == 0x11d41988 && \
1793 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001794
1795/*
1796 * mixers
1797 */
1798
Takashi Iwaid32410b12005-11-24 16:06:23 +01001799static hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001800 0x04, 0x06, 0x05, 0x0a
1801};
1802
Takashi Iwaid32410b12005-11-24 16:06:23 +01001803static hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001804 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01001805};
1806
1807/* for AD1988A revision-2, DAC2-4 are swapped */
1808static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
1809 0x04, 0x05, 0x0a, 0x06
1810};
1811
1812static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001813 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01001814};
1815
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001816static hda_nid_t ad1988_adc_nids[3] = {
1817 0x08, 0x09, 0x0f
1818};
1819
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001820static hda_nid_t ad1988_capsrc_nids[3] = {
1821 0x0c, 0x0d, 0x0e
1822};
1823
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001824#define AD1988_SPDIF_OUT 0x02
1825#define AD1988_SPDIF_IN 0x07
1826
1827static struct hda_input_mux ad1988_6stack_capture_source = {
1828 .num_items = 5,
1829 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001830 { "Front Mic", 0x1 }, /* port-B */
1831 { "Line", 0x2 }, /* port-C */
1832 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001833 { "CD", 0x5 },
1834 { "Mix", 0x9 },
1835 },
1836};
1837
1838static struct hda_input_mux ad1988_laptop_capture_source = {
1839 .num_items = 3,
1840 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001841 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001842 { "CD", 0x5 },
1843 { "Mix", 0x9 },
1844 },
1845};
1846
1847/*
1848 */
1849static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
1850 struct snd_ctl_elem_info *uinfo)
1851{
1852 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1853 struct ad198x_spec *spec = codec->spec;
1854 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
1855 spec->num_channel_mode);
1856}
1857
1858static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
1859 struct snd_ctl_elem_value *ucontrol)
1860{
1861 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1862 struct ad198x_spec *spec = codec->spec;
1863 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
1864 spec->num_channel_mode, spec->multiout.max_channels);
1865}
1866
1867static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
1868 struct snd_ctl_elem_value *ucontrol)
1869{
1870 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1871 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001872 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
1873 spec->num_channel_mode,
1874 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02001875 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02001876 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001877 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001878}
1879
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001880/* 6-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001881static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001882 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1883 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1884 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
1885 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
1886 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001887 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001888};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001889
Takashi Iwaid32410b12005-11-24 16:06:23 +01001890static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
1891 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1892 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1893 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
1894 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
1895 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001896 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001897};
1898
1899static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001900 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
1901 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
1902 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
1903 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
1904 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
1905 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
1906 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
1907
1908 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
1909 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
1910 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
1911 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
1912 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
1913 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
1914 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
1915 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
1916
1917 HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
1918 HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
1919
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001920 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001921 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
1922
1923 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
1924 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
1925
1926 { } /* end */
1927};
1928
1929/* 3-stack mode */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001930static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001931 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01001932 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001933 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
1934 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001935 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001936};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001937
Takashi Iwaid32410b12005-11-24 16:06:23 +01001938static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
1939 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001940 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
1941 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
1942 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001943 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001944};
1945
1946static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001947 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01001948 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
1949 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
1950 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001951 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
1952 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
1953
1954 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
1955 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
1956 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
1957 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
1958 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
1959 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
1960 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
1961 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
1962
1963 HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
1964 HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
1965
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001966 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001967 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
1968
1969 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
1970 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
1971 {
1972 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1973 .name = "Channel Mode",
1974 .info = ad198x_ch_mode_info,
1975 .get = ad198x_ch_mode_get,
1976 .put = ad198x_ch_mode_put,
1977 },
1978
1979 { } /* end */
1980};
1981
1982/* laptop mode */
1983static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
1984 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1985 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
1986 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
1987
1988 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
1989 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
1990 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
1991 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
1992 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
1993 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
1994
1995 HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
1996 HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
1997
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001998 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001999 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2000
2001 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
2002
2003 {
2004 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2005 .name = "External Amplifier",
Takashi Iwai18a815d2006-03-01 19:54:39 +01002006 .info = ad198x_eapd_info,
2007 .get = ad198x_eapd_get,
2008 .put = ad198x_eapd_put,
2009 .private_value = 0x12 | (1 << 8), /* port-D, inversed */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002010 },
2011
2012 { } /* end */
2013};
2014
2015/* capture */
2016static struct snd_kcontrol_new ad1988_capture_mixers[] = {
2017 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2018 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2019 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2020 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2021 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2022 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2023 {
2024 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2025 /* The multiple "Capture Source" controls confuse alsamixer
2026 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002027 */
2028 /* .name = "Capture Source", */
2029 .name = "Input Source",
2030 .count = 3,
2031 .info = ad198x_mux_enum_info,
2032 .get = ad198x_mux_enum_get,
2033 .put = ad198x_mux_enum_put,
2034 },
2035 { } /* end */
2036};
2037
2038static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2039 struct snd_ctl_elem_info *uinfo)
2040{
2041 static char *texts[] = {
2042 "PCM", "ADC1", "ADC2", "ADC3"
2043 };
2044 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2045 uinfo->count = 1;
2046 uinfo->value.enumerated.items = 4;
2047 if (uinfo->value.enumerated.item >= 4)
2048 uinfo->value.enumerated.item = 3;
2049 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2050 return 0;
2051}
2052
2053static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2054 struct snd_ctl_elem_value *ucontrol)
2055{
2056 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2057 unsigned int sel;
2058
Takashi Iwaibddcf542007-07-24 18:04:05 +02002059 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2060 AC_AMP_GET_INPUT);
2061 if (!(sel & 0x80))
2062 ucontrol->value.enumerated.item[0] = 0;
2063 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002064 sel = snd_hda_codec_read(codec, 0x0b, 0,
2065 AC_VERB_GET_CONNECT_SEL, 0);
2066 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002067 sel++;
2068 else
2069 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002070 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002071 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002072 return 0;
2073}
2074
2075static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2076 struct snd_ctl_elem_value *ucontrol)
2077{
2078 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002079 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002080 int change;
2081
Takashi Iwai35b26722007-05-05 12:17:17 +02002082 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002083 if (val > 3)
2084 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002085 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002086 sel = snd_hda_codec_read(codec, 0x1d, 0,
2087 AC_VERB_GET_AMP_GAIN_MUTE,
2088 AC_AMP_GET_INPUT);
2089 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002090 if (change) {
2091 snd_hda_codec_write_cache(codec, 0x1d, 0,
2092 AC_VERB_SET_AMP_GAIN_MUTE,
2093 AMP_IN_UNMUTE(0));
2094 snd_hda_codec_write_cache(codec, 0x1d, 0,
2095 AC_VERB_SET_AMP_GAIN_MUTE,
2096 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002097 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002098 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002099 sel = snd_hda_codec_read(codec, 0x1d, 0,
2100 AC_VERB_GET_AMP_GAIN_MUTE,
2101 AC_AMP_GET_INPUT | 0x01);
2102 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002103 if (change) {
2104 snd_hda_codec_write_cache(codec, 0x1d, 0,
2105 AC_VERB_SET_AMP_GAIN_MUTE,
2106 AMP_IN_MUTE(0));
2107 snd_hda_codec_write_cache(codec, 0x1d, 0,
2108 AC_VERB_SET_AMP_GAIN_MUTE,
2109 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002110 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002111 sel = snd_hda_codec_read(codec, 0x0b, 0,
2112 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2113 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002114 if (change)
2115 snd_hda_codec_write_cache(codec, 0x0b, 0,
2116 AC_VERB_SET_CONNECT_SEL,
2117 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002118 }
2119 return change;
2120}
2121
2122static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
2123 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2124 {
2125 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2126 .name = "IEC958 Playback Source",
2127 .info = ad1988_spdif_playback_source_info,
2128 .get = ad1988_spdif_playback_source_get,
2129 .put = ad1988_spdif_playback_source_put,
2130 },
2131 { } /* end */
2132};
2133
2134static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
2135 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2136 { } /* end */
2137};
2138
2139
2140/*
2141 * initialization verbs
2142 */
2143
2144/*
2145 * for 6-stack (+dig)
2146 */
2147static struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002148 /* Front, Surround, CLFE, side DAC; unmute as default */
2149 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2150 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2151 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2152 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002153 /* Port-A front headphon path */
2154 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2155 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2156 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2157 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2158 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2159 /* Port-D line-out path */
2160 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2161 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2162 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2163 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2164 /* Port-F surround path */
2165 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2166 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2167 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2168 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2169 /* Port-G CLFE path */
2170 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2171 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2172 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2173 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2174 /* Port-H side path */
2175 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2176 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2177 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2178 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2179 /* Mono out path */
2180 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2181 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2182 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2183 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2184 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2185 /* Port-B front mic-in path */
2186 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2187 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2188 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2189 /* Port-C line-in path */
2190 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2191 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2192 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2193 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2194 /* Port-E mic-in path */
2195 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2196 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2197 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2198 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002199 /* Analog CD Input */
2200 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002201
2202 { }
2203};
2204
2205static struct hda_verb ad1988_capture_init_verbs[] = {
2206 /* mute analog mix */
2207 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2208 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2209 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2210 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2211 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2212 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2213 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2214 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2215 /* select ADCs - front-mic */
2216 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2217 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2218 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
2219 /* ADCs; muted */
2220 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2221 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2222 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2223
2224 { }
2225};
2226
2227static struct hda_verb ad1988_spdif_init_verbs[] = {
2228 /* SPDIF out sel */
2229 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2230 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2231 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002232 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002233 /* SPDIF out pin */
2234 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002235
2236 { }
2237};
2238
2239/*
2240 * verbs for 3stack (+dig)
2241 */
2242static struct hda_verb ad1988_3stack_ch2_init[] = {
2243 /* set port-C to line-in */
2244 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2245 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2246 /* set port-E to mic-in */
2247 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2248 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2249 { } /* end */
2250};
2251
2252static struct hda_verb ad1988_3stack_ch6_init[] = {
2253 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002254 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002255 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002256 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002257 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002258 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002259 { } /* end */
2260};
2261
2262static struct hda_channel_mode ad1988_3stack_modes[2] = {
2263 { 2, ad1988_3stack_ch2_init },
2264 { 6, ad1988_3stack_ch6_init },
2265};
2266
2267static struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002268 /* Front, Surround, CLFE, side DAC; unmute as default */
2269 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2270 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2271 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2272 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002273 /* Port-A front headphon path */
2274 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2275 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2276 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2277 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2278 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2279 /* Port-D line-out path */
2280 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2281 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2282 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2283 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2284 /* Mono out path */
2285 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2286 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2287 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2288 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2289 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2290 /* Port-B front mic-in path */
2291 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2292 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2293 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002294 /* Port-C line-in/surround path - 6ch mode as default */
2295 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2296 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002297 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002298 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002299 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002300 /* Port-E mic-in/CLFE path - 6ch mode as default */
2301 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2302 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002303 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002304 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002305 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2306 /* mute analog mix */
2307 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2308 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2309 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2310 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2311 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2312 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2313 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2314 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2315 /* select ADCs - front-mic */
2316 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2317 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2318 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
2319 /* ADCs; muted */
2320 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2321 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2322 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2323 { }
2324};
2325
2326/*
2327 * verbs for laptop mode (+dig)
2328 */
2329static struct hda_verb ad1988_laptop_hp_on[] = {
2330 /* unmute port-A and mute port-D */
2331 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2332 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2333 { } /* end */
2334};
2335static struct hda_verb ad1988_laptop_hp_off[] = {
2336 /* mute port-A and unmute port-D */
2337 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2338 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2339 { } /* end */
2340};
2341
2342#define AD1988_HP_EVENT 0x01
2343
2344static struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002345 /* Front, Surround, CLFE, side DAC; unmute as default */
2346 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2347 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2348 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2349 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002350 /* Port-A front headphon path */
2351 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2352 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2353 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2354 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2355 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2356 /* unsolicited event for pin-sense */
2357 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2358 /* Port-D line-out path + EAPD */
2359 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2360 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2361 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2362 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2363 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2364 /* Mono out path */
2365 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2366 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2367 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2368 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2369 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2370 /* Port-B mic-in path */
2371 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2372 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2373 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2374 /* Port-C docking station - try to output */
2375 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2376 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2377 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2378 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2379 /* mute analog mix */
2380 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2381 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2382 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2383 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2384 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2385 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2386 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2387 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2388 /* select ADCs - mic */
2389 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2390 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2391 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
2392 /* ADCs; muted */
2393 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2394 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2395 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2396 { }
2397};
2398
2399static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2400{
2401 if ((res >> 26) != AD1988_HP_EVENT)
2402 return;
2403 if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31))
2404 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2405 else
2406 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2407}
2408
Takashi Iwaicb53c622007-08-10 17:21:45 +02002409#ifdef CONFIG_SND_HDA_POWER_SAVE
2410static struct hda_amp_list ad1988_loopbacks[] = {
2411 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2412 { 0x20, HDA_INPUT, 1 }, /* Line */
2413 { 0x20, HDA_INPUT, 4 }, /* Mic */
2414 { 0x20, HDA_INPUT, 6 }, /* CD */
2415 { } /* end */
2416};
2417#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002418
2419/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002420 * Automatic parse of I/O pins from the BIOS configuration
2421 */
2422
2423#define NUM_CONTROL_ALLOC 32
2424#define NUM_VERB_ALLOC 32
2425
2426enum {
2427 AD_CTL_WIDGET_VOL,
2428 AD_CTL_WIDGET_MUTE,
2429 AD_CTL_BIND_MUTE,
2430};
2431static struct snd_kcontrol_new ad1988_control_templates[] = {
2432 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2433 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2434 HDA_BIND_MUTE(NULL, 0, 0, 0),
2435};
2436
2437/* add dynamic controls */
2438static int add_control(struct ad198x_spec *spec, int type, const char *name,
2439 unsigned long val)
2440{
2441 struct snd_kcontrol_new *knew;
2442
2443 if (spec->num_kctl_used >= spec->num_kctl_alloc) {
2444 int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
2445
2446 knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
2447 if (! knew)
2448 return -ENOMEM;
2449 if (spec->kctl_alloc) {
2450 memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
2451 kfree(spec->kctl_alloc);
2452 }
2453 spec->kctl_alloc = knew;
2454 spec->num_kctl_alloc = num;
2455 }
2456
2457 knew = &spec->kctl_alloc[spec->num_kctl_used];
2458 *knew = ad1988_control_templates[type];
2459 knew->name = kstrdup(name, GFP_KERNEL);
2460 if (! knew->name)
2461 return -ENOMEM;
2462 knew->private_value = val;
2463 spec->num_kctl_used++;
2464 return 0;
2465}
2466
2467#define AD1988_PIN_CD_NID 0x18
2468#define AD1988_PIN_BEEP_NID 0x10
2469
2470static hda_nid_t ad1988_mixer_nids[8] = {
2471 /* A B C D E F G H */
2472 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2473};
2474
2475static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2476{
2477 static hda_nid_t idx_to_dac[8] = {
2478 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002479 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002480 };
2481 static hda_nid_t idx_to_dac_rev2[8] = {
2482 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002483 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002484 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002485 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002486 return idx_to_dac_rev2[idx];
2487 else
2488 return idx_to_dac[idx];
2489}
2490
2491static hda_nid_t ad1988_boost_nids[8] = {
2492 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2493};
2494
2495static int ad1988_pin_idx(hda_nid_t nid)
2496{
2497 static hda_nid_t ad1988_io_pins[8] = {
2498 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2499 };
2500 int i;
2501 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2502 if (ad1988_io_pins[i] == nid)
2503 return i;
2504 return 0; /* should be -1 */
2505}
2506
2507static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2508{
2509 static int loopback_idx[8] = {
2510 2, 0, 1, 3, 4, 5, 1, 4
2511 };
2512 switch (nid) {
2513 case AD1988_PIN_CD_NID:
2514 return 6;
2515 default:
2516 return loopback_idx[ad1988_pin_idx(nid)];
2517 }
2518}
2519
2520static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2521{
2522 static int adc_idx[8] = {
2523 0, 1, 2, 8, 4, 3, 6, 7
2524 };
2525 switch (nid) {
2526 case AD1988_PIN_CD_NID:
2527 return 5;
2528 default:
2529 return adc_idx[ad1988_pin_idx(nid)];
2530 }
2531}
2532
2533/* fill in the dac_nids table from the parsed pin configuration */
2534static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2535 const struct auto_pin_cfg *cfg)
2536{
2537 struct ad198x_spec *spec = codec->spec;
2538 int i, idx;
2539
2540 spec->multiout.dac_nids = spec->private_dac_nids;
2541
2542 /* check the pins hardwired to audio widget */
2543 for (i = 0; i < cfg->line_outs; i++) {
2544 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
2545 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
2546 }
2547 spec->multiout.num_dacs = cfg->line_outs;
2548 return 0;
2549}
2550
2551/* add playback controls from the parsed DAC table */
2552static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2553 const struct auto_pin_cfg *cfg)
2554{
2555 char name[32];
2556 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
2557 hda_nid_t nid;
2558 int i, err;
2559
2560 for (i = 0; i < cfg->line_outs; i++) {
2561 hda_nid_t dac = spec->multiout.dac_nids[i];
2562 if (! dac)
2563 continue;
2564 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2565 if (i == 2) {
2566 /* Center/LFE */
2567 err = add_control(spec, AD_CTL_WIDGET_VOL,
2568 "Center Playback Volume",
2569 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2570 if (err < 0)
2571 return err;
2572 err = add_control(spec, AD_CTL_WIDGET_VOL,
2573 "LFE Playback Volume",
2574 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2575 if (err < 0)
2576 return err;
2577 err = add_control(spec, AD_CTL_BIND_MUTE,
2578 "Center Playback Switch",
2579 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2580 if (err < 0)
2581 return err;
2582 err = add_control(spec, AD_CTL_BIND_MUTE,
2583 "LFE Playback Switch",
2584 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2585 if (err < 0)
2586 return err;
2587 } else {
2588 sprintf(name, "%s Playback Volume", chname[i]);
2589 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2590 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2591 if (err < 0)
2592 return err;
2593 sprintf(name, "%s Playback Switch", chname[i]);
2594 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2595 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2596 if (err < 0)
2597 return err;
2598 }
2599 }
2600 return 0;
2601}
2602
2603/* add playback controls for speaker and HP outputs */
2604static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2605 const char *pfx)
2606{
2607 struct ad198x_spec *spec = codec->spec;
2608 hda_nid_t nid;
2609 int idx, err;
2610 char name[32];
2611
2612 if (! pin)
2613 return 0;
2614
2615 idx = ad1988_pin_idx(pin);
2616 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai82bc9552006-03-21 11:24:42 +01002617 /* specify the DAC as the extra output */
2618 if (! spec->multiout.hp_nid)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002619 spec->multiout.hp_nid = nid;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002620 else
2621 spec->multiout.extra_out_nid[0] = nid;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002622 /* control HP volume/switch on the output mixer amp */
2623 sprintf(name, "%s Playback Volume", pfx);
2624 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2625 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
2626 return err;
2627 nid = ad1988_mixer_nids[idx];
2628 sprintf(name, "%s Playback Switch", pfx);
2629 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2630 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2631 return err;
2632 return 0;
2633}
2634
2635/* create input playback/capture controls for the given pin */
2636static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
2637 const char *ctlname, int boost)
2638{
2639 char name[32];
2640 int err, idx;
2641
2642 sprintf(name, "%s Playback Volume", ctlname);
2643 idx = ad1988_pin_to_loopback_idx(pin);
2644 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2645 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2646 return err;
2647 sprintf(name, "%s Playback Switch", ctlname);
2648 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2649 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2650 return err;
2651 if (boost) {
2652 hda_nid_t bnid;
2653 idx = ad1988_pin_idx(pin);
2654 bnid = ad1988_boost_nids[idx];
2655 if (bnid) {
2656 sprintf(name, "%s Boost", ctlname);
2657 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2658 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2659
2660 }
2661 }
2662 return 0;
2663}
2664
2665/* create playback/capture controls for input pins */
2666static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
2667 const struct auto_pin_cfg *cfg)
2668{
Takashi Iwaid32410b12005-11-24 16:06:23 +01002669 struct hda_input_mux *imux = &spec->private_imux;
2670 int i, err;
2671
2672 for (i = 0; i < AUTO_PIN_LAST; i++) {
Takashi Iwai4a471b72005-12-07 13:56:29 +01002673 err = new_analog_input(spec, cfg->input_pins[i],
2674 auto_pin_cfg_labels[i],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002675 i <= AUTO_PIN_FRONT_MIC);
2676 if (err < 0)
2677 return err;
Takashi Iwai4a471b72005-12-07 13:56:29 +01002678 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002679 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
2680 imux->num_items++;
2681 }
2682 imux->items[imux->num_items].label = "Mix";
2683 imux->items[imux->num_items].index = 9;
2684 imux->num_items++;
2685
2686 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
2687 "Analog Mix Playback Volume",
2688 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2689 return err;
2690 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
2691 "Analog Mix Playback Switch",
2692 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
2693 return err;
2694
2695 return 0;
2696}
2697
2698static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
2699 hda_nid_t nid, int pin_type,
2700 int dac_idx)
2701{
2702 /* set as output */
2703 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
2704 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
2705 switch (nid) {
2706 case 0x11: /* port-A - DAC 04 */
2707 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2708 break;
2709 case 0x14: /* port-B - DAC 06 */
2710 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
2711 break;
2712 case 0x15: /* port-C - DAC 05 */
2713 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
2714 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002715 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002716 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2717 break;
2718 case 0x13: /* mono - DAC 04 */
2719 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
2720 break;
2721 }
2722}
2723
2724static void ad1988_auto_init_multi_out(struct hda_codec *codec)
2725{
2726 struct ad198x_spec *spec = codec->spec;
2727 int i;
2728
2729 for (i = 0; i < spec->autocfg.line_outs; i++) {
2730 hda_nid_t nid = spec->autocfg.line_out_pins[i];
2731 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
2732 }
2733}
2734
2735static void ad1988_auto_init_extra_out(struct hda_codec *codec)
2736{
2737 struct ad198x_spec *spec = codec->spec;
2738 hda_nid_t pin;
2739
Takashi Iwai82bc9552006-03-21 11:24:42 +01002740 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002741 if (pin) /* connect to front */
2742 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002743 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01002744 if (pin) /* connect to front */
2745 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
2746}
2747
2748static void ad1988_auto_init_analog_input(struct hda_codec *codec)
2749{
2750 struct ad198x_spec *spec = codec->spec;
2751 int i, idx;
2752
2753 for (i = 0; i < AUTO_PIN_LAST; i++) {
2754 hda_nid_t nid = spec->autocfg.input_pins[i];
2755 if (! nid)
2756 continue;
2757 switch (nid) {
2758 case 0x15: /* port-C */
2759 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2760 break;
2761 case 0x17: /* port-E */
2762 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
2763 break;
2764 }
2765 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2766 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
2767 if (nid != AD1988_PIN_CD_NID)
2768 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
2769 AMP_OUT_MUTE);
2770 idx = ad1988_pin_idx(nid);
2771 if (ad1988_boost_nids[idx])
2772 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
2773 AC_VERB_SET_AMP_GAIN_MUTE,
2774 AMP_OUT_ZERO);
2775 }
2776}
2777
2778/* parse the BIOS configuration and set up the alc_spec */
2779/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
2780static int ad1988_parse_auto_config(struct hda_codec *codec)
2781{
2782 struct ad198x_spec *spec = codec->spec;
2783 int err;
2784
Kailang Yangdf694da2005-12-05 19:42:22 +01002785 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002786 return err;
2787 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
2788 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01002789 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002790 return 0; /* can't find valid BIOS pin config */
2791 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01002792 (err = ad1988_auto_create_extra_out(codec,
2793 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002794 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02002795 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01002796 "Headphone")) < 0 ||
2797 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
2798 return err;
2799
2800 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2801
2802 if (spec->autocfg.dig_out_pin)
2803 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2804 if (spec->autocfg.dig_in_pin)
2805 spec->dig_in_nid = AD1988_SPDIF_IN;
2806
2807 if (spec->kctl_alloc)
2808 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
2809
2810 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
2811
2812 spec->input_mux = &spec->private_imux;
2813
2814 return 1;
2815}
2816
2817/* init callback for auto-configuration model -- overriding the default init */
2818static int ad1988_auto_init(struct hda_codec *codec)
2819{
2820 ad198x_init(codec);
2821 ad1988_auto_init_multi_out(codec);
2822 ad1988_auto_init_extra_out(codec);
2823 ad1988_auto_init_analog_input(codec);
2824 return 0;
2825}
2826
2827
2828/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002829 */
2830
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002831static const char *ad1988_models[AD1988_MODEL_LAST] = {
2832 [AD1988_6STACK] = "6stack",
2833 [AD1988_6STACK_DIG] = "6stack-dig",
2834 [AD1988_3STACK] = "3stack",
2835 [AD1988_3STACK_DIG] = "3stack-dig",
2836 [AD1988_LAPTOP] = "laptop",
2837 [AD1988_LAPTOP_DIG] = "laptop-dig",
2838 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002839};
2840
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002841static struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002842 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002843 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002844 {}
2845};
2846
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002847static int patch_ad1988(struct hda_codec *codec)
2848{
2849 struct ad198x_spec *spec;
2850 int board_config;
2851
2852 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2853 if (spec == NULL)
2854 return -ENOMEM;
2855
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002856 codec->spec = spec;
2857
Takashi Iwai1a806f42006-07-03 15:58:16 +02002858 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002859 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
2860
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002861 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002862 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002863 if (board_config < 0) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002864 printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n");
2865 board_config = AD1988_AUTO;
2866 }
2867
2868 if (board_config == AD1988_AUTO) {
2869 /* automatic parse from the BIOS config */
2870 int err = ad1988_parse_auto_config(codec);
2871 if (err < 0) {
2872 ad198x_free(codec);
2873 return err;
2874 } else if (! err) {
2875 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
2876 board_config = AD1988_6STACK;
2877 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002878 }
2879
2880 switch (board_config) {
2881 case AD1988_6STACK:
2882 case AD1988_6STACK_DIG:
2883 spec->multiout.max_channels = 8;
2884 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002885 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002886 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
2887 else
2888 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002889 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002890 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002891 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002892 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
2893 else
2894 spec->mixers[0] = ad1988_6stack_mixers1;
2895 spec->mixers[1] = ad1988_6stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002896 spec->num_init_verbs = 1;
2897 spec->init_verbs[0] = ad1988_6stack_init_verbs;
2898 if (board_config == AD1988_6STACK_DIG) {
2899 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2900 spec->dig_in_nid = AD1988_SPDIF_IN;
2901 }
2902 break;
2903 case AD1988_3STACK:
2904 case AD1988_3STACK_DIG:
2905 spec->multiout.max_channels = 6;
2906 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002907 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002908 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
2909 else
2910 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002911 spec->input_mux = &ad1988_6stack_capture_source;
2912 spec->channel_mode = ad1988_3stack_modes;
2913 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002914 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002915 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002916 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
2917 else
2918 spec->mixers[0] = ad1988_3stack_mixers1;
2919 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002920 spec->num_init_verbs = 1;
2921 spec->init_verbs[0] = ad1988_3stack_init_verbs;
2922 if (board_config == AD1988_3STACK_DIG)
2923 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2924 break;
2925 case AD1988_LAPTOP:
2926 case AD1988_LAPTOP_DIG:
2927 spec->multiout.max_channels = 2;
2928 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002929 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002930 spec->input_mux = &ad1988_laptop_capture_source;
2931 spec->num_mixers = 1;
2932 spec->mixers[0] = ad1988_laptop_mixers;
2933 spec->num_init_verbs = 1;
2934 spec->init_verbs[0] = ad1988_laptop_init_verbs;
2935 if (board_config == AD1988_LAPTOP_DIG)
2936 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2937 break;
2938 }
2939
Takashi Iwaid32410b12005-11-24 16:06:23 +01002940 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
2941 spec->adc_nids = ad1988_adc_nids;
2942 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002943 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
2944 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
2945 if (spec->multiout.dig_out_nid) {
2946 spec->mixers[spec->num_mixers++] = ad1988_spdif_out_mixers;
2947 spec->init_verbs[spec->num_init_verbs++] = ad1988_spdif_init_verbs;
2948 }
2949 if (spec->dig_in_nid)
2950 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
2951
2952 codec->patch_ops = ad198x_patch_ops;
2953 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002954 case AD1988_AUTO:
2955 codec->patch_ops.init = ad1988_auto_init;
2956 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002957 case AD1988_LAPTOP:
2958 case AD1988_LAPTOP_DIG:
2959 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
2960 break;
2961 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02002962#ifdef CONFIG_SND_HDA_POWER_SAVE
2963 spec->loopback.amplist = ad1988_loopbacks;
2964#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01002965 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002966
2967 return 0;
2968}
2969
2970
2971/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02002972 * AD1884 / AD1984
2973 *
2974 * port-B - front line/mic-in
2975 * port-E - aux in/out
2976 * port-F - aux in/out
2977 * port-C - rear line/mic-in
2978 * port-D - rear line/hp-out
2979 * port-A - front line/hp-out
2980 *
2981 * AD1984 = AD1884 + two digital mic-ins
2982 *
2983 * FIXME:
2984 * For simplicity, we share the single DAC for both HP and line-outs
2985 * right now. The inidividual playbacks could be easily implemented,
2986 * but no build-up framework is given, so far.
2987 */
2988
2989static hda_nid_t ad1884_dac_nids[1] = {
2990 0x04,
2991};
2992
2993static hda_nid_t ad1884_adc_nids[2] = {
2994 0x08, 0x09,
2995};
2996
2997static hda_nid_t ad1884_capsrc_nids[2] = {
2998 0x0c, 0x0d,
2999};
3000
3001#define AD1884_SPDIF_OUT 0x02
3002
3003static struct hda_input_mux ad1884_capture_source = {
3004 .num_items = 4,
3005 .items = {
3006 { "Front Mic", 0x0 },
3007 { "Mic", 0x1 },
3008 { "CD", 0x2 },
3009 { "Mix", 0x3 },
3010 },
3011};
3012
3013static struct snd_kcontrol_new ad1884_base_mixers[] = {
3014 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3015 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3016 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3017 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3018 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3019 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3020 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3021 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3022 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3023 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3024 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3025 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
3026 /*
3027 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
3028 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
3029 HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
3030 HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
3031 */
3032 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
3033 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3034 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3035 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3036 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3037 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3038 {
3039 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3040 /* The multiple "Capture Source" controls confuse alsamixer
3041 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003042 */
3043 /* .name = "Capture Source", */
3044 .name = "Input Source",
3045 .count = 2,
3046 .info = ad198x_mux_enum_info,
3047 .get = ad198x_mux_enum_get,
3048 .put = ad198x_mux_enum_put,
3049 },
3050 /* SPDIF controls */
3051 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3052 {
3053 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3054 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3055 /* identical with ad1983 */
3056 .info = ad1983_spdif_route_info,
3057 .get = ad1983_spdif_route_get,
3058 .put = ad1983_spdif_route_put,
3059 },
3060 { } /* end */
3061};
3062
3063static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
3064 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3065 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3066 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003067 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003068 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003069 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003070 { } /* end */
3071};
3072
3073/*
3074 * initialization verbs
3075 */
3076static struct hda_verb ad1884_init_verbs[] = {
3077 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003078 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3079 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003080 /* Port-A (HP) mixer */
3081 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3082 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3083 /* Port-A pin */
3084 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3085 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3086 /* HP selector - select DAC2 */
3087 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3088 /* Port-D (Line-out) mixer */
3089 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3090 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3091 /* Port-D pin */
3092 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3093 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3094 /* Mono-out mixer */
3095 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3096 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3097 /* Mono-out pin */
3098 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3099 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3100 /* Mono selector */
3101 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3102 /* Port-B (front mic) pin */
3103 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3104 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3105 /* Port-C (rear mic) pin */
3106 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3107 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3108 /* Analog mixer; mute as default */
3109 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3110 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3111 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3112 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3113 /* Analog Mix output amp */
3114 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3115 /* SPDIF output selector */
3116 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3117 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3118 { } /* end */
3119};
3120
Takashi Iwaicb53c622007-08-10 17:21:45 +02003121#ifdef CONFIG_SND_HDA_POWER_SAVE
3122static struct hda_amp_list ad1884_loopbacks[] = {
3123 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3124 { 0x20, HDA_INPUT, 1 }, /* Mic */
3125 { 0x20, HDA_INPUT, 2 }, /* CD */
3126 { 0x20, HDA_INPUT, 4 }, /* Docking */
3127 { } /* end */
3128};
3129#endif
3130
Takashi Iwai2134ea42008-01-10 16:53:55 +01003131static const char *ad1884_slave_vols[] = {
3132 "PCM Playback Volume",
3133 "Mic Playback Volume",
3134 "Mono Playback Volume",
3135 "Front Mic Playback Volume",
3136 "Mic Playback Volume",
3137 "CD Playback Volume",
3138 "Internal Mic Playback Volume",
3139 "Docking Mic Playback Volume"
3140 "Beep Playback Volume",
Takashi Iwai4806ef02008-01-26 09:58:13 +01003141 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003142 NULL
3143};
3144
Takashi Iwai2bac6472007-05-18 18:21:41 +02003145static int patch_ad1884(struct hda_codec *codec)
3146{
3147 struct ad198x_spec *spec;
3148
3149 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3150 if (spec == NULL)
3151 return -ENOMEM;
3152
3153 mutex_init(&spec->amp_mutex);
3154 codec->spec = spec;
3155
3156 spec->multiout.max_channels = 2;
3157 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3158 spec->multiout.dac_nids = ad1884_dac_nids;
3159 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3160 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3161 spec->adc_nids = ad1884_adc_nids;
3162 spec->capsrc_nids = ad1884_capsrc_nids;
3163 spec->input_mux = &ad1884_capture_source;
3164 spec->num_mixers = 1;
3165 spec->mixers[0] = ad1884_base_mixers;
3166 spec->num_init_verbs = 1;
3167 spec->init_verbs[0] = ad1884_init_verbs;
3168 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003169#ifdef CONFIG_SND_HDA_POWER_SAVE
3170 spec->loopback.amplist = ad1884_loopbacks;
3171#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003172 spec->vmaster_nid = 0x04;
3173 /* we need to cover all playback volumes */
3174 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003175
3176 codec->patch_ops = ad198x_patch_ops;
3177
3178 return 0;
3179}
3180
3181/*
3182 * Lenovo Thinkpad T61/X61
3183 */
3184static struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwai3b194402007-06-04 18:32:23 +02003185 .num_items = 3,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003186 .items = {
3187 { "Mic", 0x0 },
3188 { "Internal Mic", 0x1 },
3189 { "Mix", 0x3 },
3190 },
3191};
3192
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003193
3194/*
3195 * Dell Precision T3400
3196 */
3197static struct hda_input_mux ad1984_dell_desktop_capture_source = {
3198 .num_items = 3,
3199 .items = {
3200 { "Front Mic", 0x0 },
3201 { "Line-In", 0x1 },
3202 { "Mix", 0x3 },
3203 },
3204};
3205
3206
Takashi Iwai2bac6472007-05-18 18:21:41 +02003207static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
3208 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3209 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3210 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3211 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3212 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3213 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3214 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3215 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3216 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3217 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003218 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
3219 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
Takashi Iwai0ba79622007-05-23 16:27:32 +02003220 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003221 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3222 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003223 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3224 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3225 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3226 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3227 {
3228 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3229 /* The multiple "Capture Source" controls confuse alsamixer
3230 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003231 */
3232 /* .name = "Capture Source", */
3233 .name = "Input Source",
3234 .count = 2,
3235 .info = ad198x_mux_enum_info,
3236 .get = ad198x_mux_enum_get,
3237 .put = ad198x_mux_enum_put,
3238 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003239 /* SPDIF controls */
3240 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3241 {
3242 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3243 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3244 /* identical with ad1983 */
3245 .info = ad1983_spdif_route_info,
3246 .get = ad1983_spdif_route_get,
3247 .put = ad1983_spdif_route_put,
3248 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003249 { } /* end */
3250};
3251
3252/* additional verbs */
3253static struct hda_verb ad1984_thinkpad_init_verbs[] = {
3254 /* Port-E (docking station mic) pin */
3255 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3256 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3257 /* docking mic boost */
3258 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3259 /* Analog mixer - docking mic; mute as default */
3260 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003261 /* enable EAPD bit */
3262 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003263 { } /* end */
3264};
3265
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003266/*
3267 * Dell Precision T3400
3268 */
3269static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
3270 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3271 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3272 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3273 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3274 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3275 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3276 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3277 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3278 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
3279 /*
3280 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
3281 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
3282 */
3283 HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
3284 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
3285 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3286 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3287 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3288 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3289 {
3290 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3291 /* The multiple "Capture Source" controls confuse alsamixer
3292 * So call somewhat different..
3293 */
3294 /* .name = "Capture Source", */
3295 .name = "Input Source",
3296 .count = 2,
3297 .info = ad198x_mux_enum_info,
3298 .get = ad198x_mux_enum_get,
3299 .put = ad198x_mux_enum_put,
3300 },
3301 { } /* end */
3302};
3303
Takashi Iwai2bac6472007-05-18 18:21:41 +02003304/* Digial MIC ADC NID 0x05 + 0x06 */
3305static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3306 struct hda_codec *codec,
3307 unsigned int stream_tag,
3308 unsigned int format,
3309 struct snd_pcm_substream *substream)
3310{
3311 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3312 stream_tag, 0, format);
3313 return 0;
3314}
3315
3316static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3317 struct hda_codec *codec,
3318 struct snd_pcm_substream *substream)
3319{
3320 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3321 0, 0, 0);
3322 return 0;
3323}
3324
3325static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
3326 .substreams = 2,
3327 .channels_min = 2,
3328 .channels_max = 2,
3329 .nid = 0x05,
3330 .ops = {
3331 .prepare = ad1984_pcm_dmic_prepare,
3332 .cleanup = ad1984_pcm_dmic_cleanup
3333 },
3334};
3335
3336static int ad1984_build_pcms(struct hda_codec *codec)
3337{
3338 struct ad198x_spec *spec = codec->spec;
3339 struct hda_pcm *info;
3340 int err;
3341
3342 err = ad198x_build_pcms(codec);
3343 if (err < 0)
3344 return err;
3345
3346 info = spec->pcm_rec + codec->num_pcms;
3347 codec->num_pcms++;
3348 info->name = "AD1984 Digital Mic";
3349 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3350 return 0;
3351}
3352
3353/* models */
3354enum {
3355 AD1984_BASIC,
3356 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003357 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003358 AD1984_MODELS
3359};
3360
3361static const char *ad1984_models[AD1984_MODELS] = {
3362 [AD1984_BASIC] = "basic",
3363 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003364 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003365};
3366
3367static struct snd_pci_quirk ad1984_cfg_tbl[] = {
3368 /* Lenovo Thinkpad T61/X61 */
3369 SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003370 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003371 {}
3372};
3373
3374static int patch_ad1984(struct hda_codec *codec)
3375{
3376 struct ad198x_spec *spec;
3377 int board_config, err;
3378
3379 err = patch_ad1884(codec);
3380 if (err < 0)
3381 return err;
3382 spec = codec->spec;
3383 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3384 ad1984_models, ad1984_cfg_tbl);
3385 switch (board_config) {
3386 case AD1984_BASIC:
3387 /* additional digital mics */
3388 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3389 codec->patch_ops.build_pcms = ad1984_build_pcms;
3390 break;
3391 case AD1984_THINKPAD:
Jerone Youngebf00c52008-01-07 12:22:18 +01003392 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003393 spec->input_mux = &ad1984_thinkpad_capture_source;
3394 spec->mixers[0] = ad1984_thinkpad_mixers;
3395 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
3396 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003397 case AD1984_DELL_DESKTOP:
3398 spec->multiout.dig_out_nid = 0;
3399 spec->input_mux = &ad1984_dell_desktop_capture_source;
3400 spec->mixers[0] = ad1984_dell_desktop_mixers;
3401 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003402 }
3403 return 0;
3404}
3405
3406
3407/*
Takashi Iwai0ac85512007-06-20 15:46:13 +02003408 * AD1882
3409 *
3410 * port-A - front hp-out
3411 * port-B - front mic-in
3412 * port-C - rear line-in, shared surr-out (3stack)
3413 * port-D - rear line-out
3414 * port-E - rear mic-in, shared clfe-out (3stack)
3415 * port-F - rear surr-out (6stack)
3416 * port-G - rear clfe-out (6stack)
3417 */
3418
3419static hda_nid_t ad1882_dac_nids[3] = {
3420 0x04, 0x03, 0x05
3421};
3422
3423static hda_nid_t ad1882_adc_nids[2] = {
3424 0x08, 0x09,
3425};
3426
3427static hda_nid_t ad1882_capsrc_nids[2] = {
3428 0x0c, 0x0d,
3429};
3430
3431#define AD1882_SPDIF_OUT 0x02
3432
3433/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
3434static struct hda_input_mux ad1882_capture_source = {
3435 .num_items = 5,
3436 .items = {
3437 { "Front Mic", 0x1 },
3438 { "Mic", 0x4 },
3439 { "Line", 0x2 },
3440 { "CD", 0x3 },
3441 { "Mix", 0x7 },
3442 },
3443};
3444
3445static struct snd_kcontrol_new ad1882_base_mixers[] = {
3446 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3447 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
3448 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
3449 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
3450 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3451 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3452 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3453 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3454 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3455 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3456 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3457 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3458 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
3459 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
3460 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
3461 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
3462 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
3463 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
3464 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
3465 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
3466 HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
3467 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3468 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3469 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3470 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3471 {
3472 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3473 /* The multiple "Capture Source" controls confuse alsamixer
3474 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02003475 */
3476 /* .name = "Capture Source", */
3477 .name = "Input Source",
3478 .count = 2,
3479 .info = ad198x_mux_enum_info,
3480 .get = ad198x_mux_enum_get,
3481 .put = ad198x_mux_enum_put,
3482 },
3483 /* SPDIF controls */
3484 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3485 {
3486 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3487 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3488 /* identical with ad1983 */
3489 .info = ad1983_spdif_route_info,
3490 .get = ad1983_spdif_route_get,
3491 .put = ad1983_spdif_route_put,
3492 },
3493 { } /* end */
3494};
3495
3496static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
3497 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
3498 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
3499 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
3500 {
3501 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3502 .name = "Channel Mode",
3503 .info = ad198x_ch_mode_info,
3504 .get = ad198x_ch_mode_get,
3505 .put = ad198x_ch_mode_put,
3506 },
3507 { } /* end */
3508};
3509
3510static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
3511 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
3512 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
3513 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
3514 { } /* end */
3515};
3516
3517static struct hda_verb ad1882_ch2_init[] = {
3518 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3519 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3520 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3521 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3522 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3523 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3524 { } /* end */
3525};
3526
3527static struct hda_verb ad1882_ch4_init[] = {
3528 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3529 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3530 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3531 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3532 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3533 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3534 { } /* end */
3535};
3536
3537static struct hda_verb ad1882_ch6_init[] = {
3538 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3539 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3540 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3541 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3542 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3543 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3544 { } /* end */
3545};
3546
3547static struct hda_channel_mode ad1882_modes[3] = {
3548 { 2, ad1882_ch2_init },
3549 { 4, ad1882_ch4_init },
3550 { 6, ad1882_ch6_init },
3551};
3552
3553/*
3554 * initialization verbs
3555 */
3556static struct hda_verb ad1882_init_verbs[] = {
3557 /* DACs; mute as default */
3558 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3559 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3560 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3561 /* Port-A (HP) mixer */
3562 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3563 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3564 /* Port-A pin */
3565 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3566 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3567 /* HP selector - select DAC2 */
3568 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
3569 /* Port-D (Line-out) mixer */
3570 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3571 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3572 /* Port-D pin */
3573 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3574 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3575 /* Mono-out mixer */
3576 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3577 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3578 /* Mono-out pin */
3579 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3580 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3581 /* Port-B (front mic) pin */
3582 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3583 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3584 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
3585 /* Port-C (line-in) pin */
3586 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3587 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3588 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
3589 /* Port-C mixer - mute as input */
3590 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3591 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3592 /* Port-E (mic-in) pin */
3593 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3594 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3595 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
3596 /* Port-E mixer - mute as input */
3597 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3598 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3599 /* Port-F (surround) */
3600 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3601 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3602 /* Port-G (CLFE) */
3603 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3604 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3605 /* Analog mixer; mute as default */
3606 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
3607 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3608 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3609 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3610 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3611 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3612 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3613 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
3614 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
3615 /* Analog Mix output amp */
3616 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3617 /* SPDIF output selector */
3618 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3619 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3620 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3621 { } /* end */
3622};
3623
Takashi Iwaicb53c622007-08-10 17:21:45 +02003624#ifdef CONFIG_SND_HDA_POWER_SAVE
3625static struct hda_amp_list ad1882_loopbacks[] = {
3626 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3627 { 0x20, HDA_INPUT, 1 }, /* Mic */
3628 { 0x20, HDA_INPUT, 4 }, /* Line */
3629 { 0x20, HDA_INPUT, 6 }, /* CD */
3630 { } /* end */
3631};
3632#endif
3633
Takashi Iwai0ac85512007-06-20 15:46:13 +02003634/* models */
3635enum {
3636 AD1882_3STACK,
3637 AD1882_6STACK,
3638 AD1882_MODELS
3639};
3640
3641static const char *ad1882_models[AD1986A_MODELS] = {
3642 [AD1882_3STACK] = "3stack",
3643 [AD1882_6STACK] = "6stack",
3644};
3645
3646
3647static int patch_ad1882(struct hda_codec *codec)
3648{
3649 struct ad198x_spec *spec;
3650 int board_config;
3651
3652 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3653 if (spec == NULL)
3654 return -ENOMEM;
3655
3656 mutex_init(&spec->amp_mutex);
3657 codec->spec = spec;
3658
3659 spec->multiout.max_channels = 6;
3660 spec->multiout.num_dacs = 3;
3661 spec->multiout.dac_nids = ad1882_dac_nids;
3662 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
3663 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
3664 spec->adc_nids = ad1882_adc_nids;
3665 spec->capsrc_nids = ad1882_capsrc_nids;
3666 spec->input_mux = &ad1882_capture_source;
3667 spec->num_mixers = 1;
3668 spec->mixers[0] = ad1882_base_mixers;
3669 spec->num_init_verbs = 1;
3670 spec->init_verbs[0] = ad1882_init_verbs;
3671 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003672#ifdef CONFIG_SND_HDA_POWER_SAVE
3673 spec->loopback.amplist = ad1882_loopbacks;
3674#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003675 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02003676
3677 codec->patch_ops = ad198x_patch_ops;
3678
3679 /* override some parameters */
3680 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
3681 ad1882_models, NULL);
3682 switch (board_config) {
3683 default:
3684 case AD1882_3STACK:
3685 spec->num_mixers = 2;
3686 spec->mixers[1] = ad1882_3stack_mixers;
3687 spec->channel_mode = ad1882_modes;
3688 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
3689 spec->need_dac_fix = 1;
3690 spec->multiout.max_channels = 2;
3691 spec->multiout.num_dacs = 1;
3692 break;
3693 case AD1882_6STACK:
3694 spec->num_mixers = 2;
3695 spec->mixers[1] = ad1882_6stack_mixers;
3696 break;
3697 }
3698 return 0;
3699}
3700
3701
3702/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07003703 * patch entries
3704 */
3705struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02003706 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003707 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02003708 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
3709 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003710 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07003711 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003712 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02003713 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07003714 {} /* terminator */
3715};