blob: d7d636decef86a92de85238529d0dd5b1ec557e9 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002 * HD audio interface patch for AD1981HD, AD1983, AD1986A
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 *
4 * Copyright (c) 2005 Takashi Iwai <tiwai@suse.de>
5 *
6 * This driver is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This driver is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include <sound/driver.h>
22#include <linux/init.h>
23#include <linux/delay.h>
24#include <linux/slab.h>
25#include <linux/pci.h>
26#include <sound/core.h>
27#include "hda_codec.h"
28#include "hda_local.h"
29
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020030struct ad198x_spec {
Takashi Iwai985be542005-11-02 18:26:49 +010031 snd_kcontrol_new_t *mixers[5];
32 int num_mixers;
33
34 const struct hda_verb *init_verbs[3]; /* initialization verbs
35 * don't forget NULL termination!
36 */
37 unsigned int num_init_verbs;
38
39 /* playback */
40 struct hda_multi_out multiout; /* playback set-up
41 * max_channels, dacs must be set
42 * dig_out_nid and hp_nid are optional
43 */
44
45 /* capture */
46 unsigned int num_adc_nids;
47 hda_nid_t *adc_nids;
48 hda_nid_t dig_in_nid; /* digital-in NID; optional */
49
50 /* capture source */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020051 const struct hda_input_mux *input_mux;
Takashi Iwai985be542005-11-02 18:26:49 +010052 unsigned int cur_mux[3];
53
54 /* channel model */
55 const struct alc_channel_mode *channel_mode;
56 int num_channel_mode;
57
58 /* PCM information */
59 struct hda_pcm pcm_rec[2]; /* used in alc_build_pcms() */
60
61 struct semaphore amp_mutex; /* PCM volume/mute control mutex */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020062 unsigned int spdif_route;
Linus Torvalds1da177e2005-04-16 15:20:36 -070063};
64
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020065/*
66 * input MUX handling (common part)
67 */
68static int ad198x_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
69{
70 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
71 struct ad198x_spec *spec = codec->spec;
72
73 return snd_hda_input_mux_info(spec->input_mux, uinfo);
74}
75
76static int ad198x_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
77{
78 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
79 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +010080 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020081
Takashi Iwai985be542005-11-02 18:26:49 +010082 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020083 return 0;
84}
85
86static int ad198x_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
87{
88 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
89 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +010090 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020091
92 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai985be542005-11-02 18:26:49 +010093 spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020094}
95
96/*
97 * initialization (common callbacks)
98 */
99static int ad198x_init(struct hda_codec *codec)
100{
101 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100102 int i;
103
104 for (i = 0; i < spec->num_init_verbs; i++)
105 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200106 return 0;
107}
108
109static int ad198x_build_controls(struct hda_codec *codec)
110{
111 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100112 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200113 int err;
114
Takashi Iwai985be542005-11-02 18:26:49 +0100115 for (i = 0; i < spec->num_mixers; i++) {
116 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
117 if (err < 0)
118 return err;
119 }
120 if (spec->multiout.dig_out_nid) {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200121 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100122 if (err < 0)
123 return err;
124 }
125 if (spec->dig_in_nid) {
126 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
127 if (err < 0)
128 return err;
129 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200130 return 0;
131}
132
133/*
134 * Analog playback callbacks
135 */
136static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
137 struct hda_codec *codec,
138 snd_pcm_substream_t *substream)
139{
140 struct ad198x_spec *spec = codec->spec;
141 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
142}
143
144static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
145 struct hda_codec *codec,
146 unsigned int stream_tag,
147 unsigned int format,
148 snd_pcm_substream_t *substream)
149{
150 struct ad198x_spec *spec = codec->spec;
151 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
152 format, substream);
153}
154
155static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
156 struct hda_codec *codec,
157 snd_pcm_substream_t *substream)
158{
159 struct ad198x_spec *spec = codec->spec;
160 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
161}
162
163/*
164 * Digital out
165 */
166static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
167 struct hda_codec *codec,
168 snd_pcm_substream_t *substream)
169{
170 struct ad198x_spec *spec = codec->spec;
171 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
172}
173
174static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
175 struct hda_codec *codec,
176 snd_pcm_substream_t *substream)
177{
178 struct ad198x_spec *spec = codec->spec;
179 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
180}
181
182/*
183 * Analog capture
184 */
185static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
186 struct hda_codec *codec,
187 unsigned int stream_tag,
188 unsigned int format,
189 snd_pcm_substream_t *substream)
190{
191 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100192 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
193 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200194 return 0;
195}
196
197static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
198 struct hda_codec *codec,
199 snd_pcm_substream_t *substream)
200{
201 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100202 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
203 0, 0, 0);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200204 return 0;
205}
206
207
208/*
209 */
210static struct hda_pcm_stream ad198x_pcm_analog_playback = {
211 .substreams = 1,
212 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100213 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200214 .nid = 0, /* fill later */
215 .ops = {
216 .open = ad198x_playback_pcm_open,
217 .prepare = ad198x_playback_pcm_prepare,
218 .cleanup = ad198x_playback_pcm_cleanup
219 },
220};
221
222static struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100223 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200224 .channels_min = 2,
225 .channels_max = 2,
226 .nid = 0, /* fill later */
227 .ops = {
228 .prepare = ad198x_capture_pcm_prepare,
229 .cleanup = ad198x_capture_pcm_cleanup
230 },
231};
232
233static struct hda_pcm_stream ad198x_pcm_digital_playback = {
234 .substreams = 1,
235 .channels_min = 2,
236 .channels_max = 2,
237 .nid = 0, /* fill later */
238 .ops = {
239 .open = ad198x_dig_playback_pcm_open,
240 .close = ad198x_dig_playback_pcm_close
241 },
242};
243
Takashi Iwai985be542005-11-02 18:26:49 +0100244static struct hda_pcm_stream ad198x_pcm_digital_capture = {
245 .substreams = 1,
246 .channels_min = 2,
247 .channels_max = 2,
248 /* NID is set in alc_build_pcms */
249};
250
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200251static int ad198x_build_pcms(struct hda_codec *codec)
252{
253 struct ad198x_spec *spec = codec->spec;
254 struct hda_pcm *info = spec->pcm_rec;
255
256 codec->num_pcms = 1;
257 codec->pcm_info = info;
258
259 info->name = "AD198x Analog";
260 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
261 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
262 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
263 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100264 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
265 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200266
267 if (spec->multiout.dig_out_nid) {
268 info++;
269 codec->num_pcms++;
270 info->name = "AD198x Digital";
271 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
272 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100273 if (spec->dig_in_nid) {
274 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
275 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
276 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200277 }
278
279 return 0;
280}
281
282static void ad198x_free(struct hda_codec *codec)
283{
284 kfree(codec->spec);
285}
286
287#ifdef CONFIG_PM
288static int ad198x_resume(struct hda_codec *codec)
289{
290 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100291 int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200292
293 ad198x_init(codec);
Takashi Iwai985be542005-11-02 18:26:49 +0100294 for (i = 0; i < spec->num_mixers; i++)
295 snd_hda_resume_ctls(codec, spec->mixers[i]);
296 if (spec->multiout.dig_out_nid)
297 snd_hda_resume_spdif_out(codec);
298 if (spec->dig_in_nid)
299 snd_hda_resume_spdif_in(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200300 return 0;
301}
302#endif
303
304static struct hda_codec_ops ad198x_patch_ops = {
305 .build_controls = ad198x_build_controls,
306 .build_pcms = ad198x_build_pcms,
307 .init = ad198x_init,
308 .free = ad198x_free,
309#ifdef CONFIG_PM
310 .resume = ad198x_resume,
311#endif
312};
313
314
315/*
316 * AD1986A specific
317 */
318
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319#define AD1986A_SPDIF_OUT 0x02
320#define AD1986A_FRONT_DAC 0x03
321#define AD1986A_SURR_DAC 0x04
322#define AD1986A_CLFE_DAC 0x05
323#define AD1986A_ADC 0x06
324
325static hda_nid_t ad1986a_dac_nids[3] = {
326 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
327};
Takashi Iwai985be542005-11-02 18:26:49 +0100328static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
330static struct hda_input_mux ad1986a_capture_source = {
331 .num_items = 7,
332 .items = {
333 { "Mic", 0x0 },
334 { "CD", 0x1 },
335 { "Aux", 0x3 },
336 { "Line", 0x4 },
337 { "Mix", 0x5 },
338 { "Mono", 0x6 },
339 { "Phone", 0x7 },
340 },
341};
342
343/*
344 * PCM control
345 *
346 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
347 */
348
349#define ad1986a_pcm_amp_vol_info snd_hda_mixer_amp_volume_info
350
351static int ad1986a_pcm_amp_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
352{
353 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200354 struct ad198x_spec *ad = codec->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355
356 down(&ad->amp_mutex);
357 snd_hda_mixer_amp_volume_get(kcontrol, ucontrol);
358 up(&ad->amp_mutex);
359 return 0;
360}
361
362static int ad1986a_pcm_amp_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
363{
364 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200365 struct ad198x_spec *ad = codec->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 int i, change = 0;
367
368 down(&ad->amp_mutex);
369 for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) {
370 kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT);
371 change |= snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
372 }
373 kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT);
374 up(&ad->amp_mutex);
375 return change;
376}
377
Takashi Iwaiead9b7c2005-06-08 14:48:19 +0200378#define ad1986a_pcm_amp_sw_info snd_hda_mixer_amp_switch_info
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379
380static int ad1986a_pcm_amp_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
381{
382 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200383 struct ad198x_spec *ad = codec->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384
385 down(&ad->amp_mutex);
386 snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
387 up(&ad->amp_mutex);
388 return 0;
389}
390
391static int ad1986a_pcm_amp_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
392{
393 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200394 struct ad198x_spec *ad = codec->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 int i, change = 0;
396
397 down(&ad->amp_mutex);
398 for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) {
399 kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT);
400 change |= snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
401 }
402 kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT);
403 up(&ad->amp_mutex);
404 return change;
405}
406
407/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 * mixers
409 */
410static snd_kcontrol_new_t ad1986a_mixers[] = {
411 {
412 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
413 .name = "PCM Playback Volume",
414 .info = ad1986a_pcm_amp_vol_info,
415 .get = ad1986a_pcm_amp_vol_get,
416 .put = ad1986a_pcm_amp_vol_put,
417 .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)
418 },
419 {
420 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
421 .name = "PCM Playback Switch",
422 .info = ad1986a_pcm_amp_sw_info,
423 .get = ad1986a_pcm_amp_sw_get,
424 .put = ad1986a_pcm_amp_sw_put,
425 .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)
426 },
427 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
428 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
429 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
430 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
431 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
432 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
433 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
434 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
435 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
436 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
437 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
438 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
439 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
440 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
441 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
442 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
443 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
444 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
445 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
446 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
447 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
448 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
449 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
450 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
451 {
452 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
453 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200454 .info = ad198x_mux_enum_info,
455 .get = ad198x_mux_enum_get,
456 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 },
458 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
459 { } /* end */
460};
461
462/*
463 * initialization verbs
464 */
465static struct hda_verb ad1986a_init_verbs[] = {
466 /* Front, Surround, CLFE DAC; mute as default */
467 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
468 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
469 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
470 /* Downmix - off */
471 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
472 /* HP, Line-Out, Surround, CLFE selectors */
473 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
474 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
475 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
476 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
477 /* Mono selector */
478 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
479 /* Mic selector: Mic 1/2 pin */
480 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
481 /* Line-in selector: Line-in */
482 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
483 /* Mic 1/2 swap */
484 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
485 /* Record selector: mic */
486 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
487 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
488 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
489 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
490 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
491 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
492 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
493 /* PC beep */
494 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
495 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
496 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
497 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
498 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
499 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
500 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200501 /* HP Pin */
502 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
503 /* Front, Surround, CLFE Pins */
504 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
505 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
506 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
507 /* Mono Pin */
508 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
509 /* Mic Pin */
510 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
511 /* Line, Aux, CD, Beep-In Pin */
512 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
513 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
514 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
515 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
516 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 { } /* end */
518};
519
520
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521static int patch_ad1986a(struct hda_codec *codec)
522{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200523 struct ad198x_spec *spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524
Takashi Iwaie560d8d2005-09-09 14:21:46 +0200525 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 if (spec == NULL)
527 return -ENOMEM;
528
529 init_MUTEX(&spec->amp_mutex);
530 codec->spec = spec;
531
532 spec->multiout.max_channels = 6;
533 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
534 spec->multiout.dac_nids = ad1986a_dac_nids;
535 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +0100536 spec->num_adc_nids = 1;
537 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200538 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +0100539 spec->num_mixers = 1;
540 spec->mixers[0] = ad1986a_mixers;
541 spec->num_init_verbs = 1;
542 spec->init_verbs[0] = ad1986a_init_verbs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200544 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545
546 return 0;
547}
548
549/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200550 * AD1983 specific
551 */
552
553#define AD1983_SPDIF_OUT 0x02
554#define AD1983_DAC 0x03
555#define AD1983_ADC 0x04
556
557static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +0100558static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200559
560static struct hda_input_mux ad1983_capture_source = {
561 .num_items = 4,
562 .items = {
563 { "Mic", 0x0 },
564 { "Line", 0x1 },
565 { "Mix", 0x2 },
566 { "Mix Mono", 0x3 },
567 },
568};
569
570/*
571 * SPDIF playback route
572 */
573static int ad1983_spdif_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
574{
575 static char *texts[] = { "PCM", "ADC" };
576
577 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
578 uinfo->count = 1;
579 uinfo->value.enumerated.items = 2;
580 if (uinfo->value.enumerated.item > 1)
581 uinfo->value.enumerated.item = 1;
582 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
583 return 0;
584}
585
586static int ad1983_spdif_route_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
587{
588 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
589 struct ad198x_spec *spec = codec->spec;
590
591 ucontrol->value.enumerated.item[0] = spec->spdif_route;
592 return 0;
593}
594
595static int ad1983_spdif_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
596{
597 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
598 struct ad198x_spec *spec = codec->spec;
599
600 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
601 spec->spdif_route = ucontrol->value.enumerated.item[0];
602 snd_hda_codec_write(codec, spec->multiout.dig_out_nid, 0,
603 AC_VERB_SET_CONNECT_SEL, spec->spdif_route);
604 return 1;
605 }
606 return 0;
607}
608
609static snd_kcontrol_new_t ad1983_mixers[] = {
610 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
611 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
612 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
613 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
614 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
615 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
616 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
617 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
618 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
619 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
620 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
621 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
622 HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x10, 1, 0x0, HDA_OUTPUT),
623 HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x10, 1, 0x0, HDA_OUTPUT),
624 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
625 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
626 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
627 {
628 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
629 .name = "Capture Source",
630 .info = ad198x_mux_enum_info,
631 .get = ad198x_mux_enum_get,
632 .put = ad198x_mux_enum_put,
633 },
634 {
635 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Clemens Ladisch10e8d782005-08-03 13:40:08 +0200636 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200637 .info = ad1983_spdif_route_info,
638 .get = ad1983_spdif_route_get,
639 .put = ad1983_spdif_route_put,
640 },
641 { } /* end */
642};
643
644static struct hda_verb ad1983_init_verbs[] = {
645 /* Front, HP, Mono; mute as default */
646 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
647 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
648 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
649 /* Beep, PCM, Mic, Line-In: mute */
650 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
651 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
652 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
653 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
654 /* Front, HP selectors; from Mix */
655 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
656 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
657 /* Mono selector; from Mix */
658 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
659 /* Mic selector; Mic */
660 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
661 /* Line-in selector: Line-in */
662 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
663 /* Mic boost: 0dB */
664 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
665 /* Record selector: mic */
666 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
667 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
668 /* SPDIF route: PCM */
669 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
670 /* Front Pin */
671 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
672 /* HP Pin */
673 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
674 /* Mono Pin */
675 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
676 /* Mic Pin */
677 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
678 /* Line Pin */
679 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
680 { } /* end */
681};
682
Takashi Iwai985be542005-11-02 18:26:49 +0100683
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200684static int patch_ad1983(struct hda_codec *codec)
685{
686 struct ad198x_spec *spec;
687
Takashi Iwaie560d8d2005-09-09 14:21:46 +0200688 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200689 if (spec == NULL)
690 return -ENOMEM;
691
692 init_MUTEX(&spec->amp_mutex);
693 codec->spec = spec;
694
695 spec->multiout.max_channels = 2;
696 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
697 spec->multiout.dac_nids = ad1983_dac_nids;
698 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +0100699 spec->num_adc_nids = 1;
700 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200701 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +0100702 spec->num_mixers = 1;
703 spec->mixers[0] = ad1983_mixers;
704 spec->num_init_verbs = 1;
705 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200706 spec->spdif_route = 0;
707
708 codec->patch_ops = ad198x_patch_ops;
709
710 return 0;
711}
712
713
714/*
715 * AD1981 HD specific
716 */
717
718#define AD1981_SPDIF_OUT 0x02
719#define AD1981_DAC 0x03
720#define AD1981_ADC 0x04
721
722static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
Takashi Iwai985be542005-11-02 18:26:49 +0100723static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200724
725/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
726static struct hda_input_mux ad1981_capture_source = {
727 .num_items = 7,
728 .items = {
729 { "Front Mic", 0x0 },
730 { "Line", 0x1 },
731 { "Mix", 0x2 },
732 { "Mix Mono", 0x3 },
733 { "CD", 0x4 },
734 { "Mic", 0x6 },
735 { "Aux", 0x7 },
736 },
737};
738
739static snd_kcontrol_new_t ad1981_mixers[] = {
740 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
741 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
742 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
743 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
744 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
745 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
746 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
747 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
748 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
749 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
750 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
751 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
752 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
753 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
754 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
755 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
756 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
757 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
758 HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
759 HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x0d, 1, 0x0, HDA_OUTPUT),
760 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
761 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
762 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
763 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
764 {
765 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
766 .name = "Capture Source",
767 .info = ad198x_mux_enum_info,
768 .get = ad198x_mux_enum_get,
769 .put = ad198x_mux_enum_put,
770 },
771 /* identical with AD1983 */
772 {
773 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Clemens Ladisch10e8d782005-08-03 13:40:08 +0200774 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200775 .info = ad1983_spdif_route_info,
776 .get = ad1983_spdif_route_get,
777 .put = ad1983_spdif_route_put,
778 },
779 { } /* end */
780};
781
782static struct hda_verb ad1981_init_verbs[] = {
783 /* Front, HP, Mono; mute as default */
784 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
785 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
786 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
787 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
788 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
789 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
790 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
791 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
792 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
793 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
794 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
795 /* Front, HP selectors; from Mix */
796 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
797 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
798 /* Mono selector; from Mix */
799 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
800 /* Mic Mixer; select Front Mic */
801 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
802 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
803 /* Mic boost: 0dB */
804 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
805 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
806 /* Record selector: Front mic */
807 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
808 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
809 /* SPDIF route: PCM */
810 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
811 /* Front Pin */
812 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
813 /* HP Pin */
814 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
815 /* Mono Pin */
816 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
817 /* Front & Rear Mic Pins */
818 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
819 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
820 /* Line Pin */
821 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
822 /* Digital Beep */
823 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
824 /* Line-Out as Input: disabled */
825 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
826 { } /* end */
827};
828
829static int patch_ad1981(struct hda_codec *codec)
830{
831 struct ad198x_spec *spec;
832
Takashi Iwaie560d8d2005-09-09 14:21:46 +0200833 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200834 if (spec == NULL)
835 return -ENOMEM;
836
837 init_MUTEX(&spec->amp_mutex);
838 codec->spec = spec;
839
840 spec->multiout.max_channels = 2;
841 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
842 spec->multiout.dac_nids = ad1981_dac_nids;
843 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +0100844 spec->num_adc_nids = 1;
845 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200846 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +0100847 spec->num_mixers = 1;
848 spec->mixers[0] = ad1981_mixers;
849 spec->num_init_verbs = 1;
850 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200851 spec->spdif_route = 0;
852
853 codec->patch_ops = ad198x_patch_ops;
854
855 return 0;
856}
857
858
859/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 * patch entries
861 */
862struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200863 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
864 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
866 {} /* terminator */
867};