ac97.c revision 1.58 1 /* $NetBSD: ac97.c,v 1.58 2004/08/08 10:41:57 kent Exp $ */
2 /* $OpenBSD: ac97.c,v 1.8 2000/07/19 09:01:35 csapuntz Exp $ */
3
4 /*
5 * Copyright (c) 1999, 2000 Constantine Sapuntzakis
6 *
7 * Author: Constantine Sapuntzakis <csapuntz (at) stanford.edu>
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. The name of the author may not be used to endorse or promote
18 * products derived from this software without specific prior written
19 * permission.
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
21 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
30 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 * DAMAGE
32 */
33
34 /* Partially inspired by FreeBSD's sys/dev/pcm/ac97.c. It came with
35 the following copyright */
36
37 /*
38 * Copyright (c) 1999 Cameron Grant <gandalf (at) vilnya.demon.co.uk>
39 * All rights reserved.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 * notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 * notice, this list of conditions and the following disclaimer in the
48 * documentation and/or other materials provided with the distribution.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
56 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60 * SUCH DAMAGE.
61 *
62 * $FreeBSD$
63 */
64
65 #include <sys/cdefs.h>
66 __KERNEL_RCSID(0, "$NetBSD: ac97.c,v 1.58 2004/08/08 10:41:57 kent Exp $");
67
68 #include <sys/param.h>
69 #include <sys/systm.h>
70 #include <sys/kernel.h>
71 #include <sys/malloc.h>
72 #include <sys/device.h>
73
74 #include <sys/audioio.h>
75 #include <dev/audio_if.h>
76
77 #include <dev/ic/ac97reg.h>
78 #include <dev/ic/ac97var.h>
79
80 struct ac97_softc;
81 struct ac97_source_info;
82 int ac97_mixer_get_port(struct ac97_codec_if *, mixer_ctrl_t *);
83 int ac97_mixer_set_port(struct ac97_codec_if *, mixer_ctrl_t *);
84 int ac97_query_devinfo(struct ac97_codec_if *, mixer_devinfo_t *);
85 int ac97_get_portnum_by_name(struct ac97_codec_if *, const char *,
86 const char *, const char *);
87 void ac97_restore_shadow(struct ac97_codec_if *);
88 int ac97_set_rate(struct ac97_codec_if *, int, u_long *);
89 void ac97_set_clock(struct ac97_codec_if *, unsigned int);
90 u_int16_t ac97_get_extcaps(struct ac97_codec_if *);
91 int ac97_add_port(struct ac97_softc *, const struct ac97_source_info *);
92 int ac97_str_equal(const char *, const char *);
93 int ac97_check_capability(struct ac97_softc *, int);
94 void ac97_setup_source_info(struct ac97_softc *);
95 void ac97_read(struct ac97_softc *, u_int8_t, u_int16_t *);
96 void ac97_setup_defaults(struct ac97_softc *);
97 int ac97_write(struct ac97_softc *, u_int8_t, u_int16_t);
98
99 static void ac97_ad198x_init(struct ac97_softc *);
100 static void ac97_alc650_init(struct ac97_softc *);
101 static void ac97_vt1616_init(struct ac97_softc *);
102
103 #define Ac97Ntone "tone"
104 #define Ac97Nphone "phone"
105
106 static const struct audio_mixer_enum
107 ac97_on_off = { 2, { { { AudioNoff } , 0 },
108 { { AudioNon } , 1 } } };
109
110 static const struct audio_mixer_enum
111 ac97_mic_select = { 2, { { { AudioNmicrophone "0" }, 0 },
112 { { AudioNmicrophone "1" }, 1 } } };
113
114 static const struct audio_mixer_enum
115 ac97_mono_select = { 2, { { { AudioNmixerout }, 0 },
116 { { AudioNmicrophone }, 1 } } };
117
118 static const struct audio_mixer_enum
119 ac97_source = { 8, { { { AudioNmicrophone } , 0 },
120 { { AudioNcd }, 1 },
121 { { AudioNvideo }, 2 },
122 { { AudioNaux }, 3 },
123 { { AudioNline }, 4 },
124 { { AudioNmixerout }, 5 },
125 { { AudioNmixerout AudioNmono }, 6 },
126 { { Ac97Nphone }, 7 } } };
127
128 /*
129 * Due to different values for each source that uses these structures,
130 * the ac97_query_devinfo function sets delta in mixer_devinfo_t using
131 * ac97_source_info.bits.
132 */
133 static const struct audio_mixer_value
134 ac97_volume_stereo = { { AudioNvolume }, 2 };
135
136 static const struct audio_mixer_value
137 ac97_volume_mono = { { AudioNvolume }, 1 };
138
139 #define WRAP(a) &a, sizeof(a)
140
141 const struct ac97_source_info {
142 const char *class;
143 const char *device;
144 const char *qualifier;
145
146 int type;
147 const void *info;
148 int info_size;
149
150 u_int8_t reg;
151 u_int16_t default_value;
152 u_int8_t bits:3;
153 u_int8_t ofs:4;
154 u_int8_t mute:1;
155 u_int8_t polarity:1; /* Does 0 == MAX or MIN */
156 enum {
157 CHECK_NONE = 0,
158 CHECK_SURROUND,
159 CHECK_CENTER,
160 CHECK_LFE,
161 CHECK_HEADPHONES,
162 CHECK_TONE,
163 CHECK_MIC,
164 CHECK_LOUDNESS,
165 CHECK_3D
166 } req_feature;
167
168 int prev;
169 int next;
170 int mixer_class;
171 } source_info[] = {
172 { AudioCinputs, NULL, NULL,
173 AUDIO_MIXER_CLASS, },
174 { AudioCoutputs, NULL, NULL,
175 AUDIO_MIXER_CLASS, },
176 { AudioCrecord, NULL, NULL,
177 AUDIO_MIXER_CLASS, },
178 /* Stereo master volume*/
179 { AudioCoutputs, AudioNmaster, NULL,
180 AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
181 AC97_REG_MASTER_VOLUME, 0x8000, 5, 0, 1,
182 },
183 /* Mono volume */
184 { AudioCoutputs, AudioNmono, NULL,
185 AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
186 AC97_REG_MASTER_VOLUME_MONO, 0x8000, 6, 0, 1,
187 },
188 { AudioCoutputs, AudioNmono, AudioNsource,
189 AUDIO_MIXER_ENUM, WRAP(ac97_mono_select),
190 AC97_REG_GP, 0x0000, 1, 9, 0,
191 },
192 /* Headphone volume */
193 { AudioCoutputs, AudioNheadphone, NULL,
194 AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
195 AC97_REG_HEADPHONE_VOLUME, 0x8000, 6, 0, 1, 0, CHECK_HEADPHONES
196 },
197 /* Surround volume - logic hard coded for mute */
198 { AudioCoutputs, AudioNsurround, NULL,
199 AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
200 AC97_REG_SURR_MASTER, 0x8080, 5, 0, 1, 0, CHECK_SURROUND
201 },
202 /* Center volume*/
203 { AudioCoutputs, AudioNcenter, NULL,
204 AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
205 AC97_REG_CENTER_LFE_MASTER, 0x8080, 5, 0, 0, 0, CHECK_CENTER
206 },
207 { AudioCoutputs, AudioNcenter, AudioNmute,
208 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
209 AC97_REG_CENTER_LFE_MASTER, 0x8080, 1, 7, 0, 0, CHECK_CENTER
210 },
211 /* LFE volume*/
212 { AudioCoutputs, AudioNlfe, NULL,
213 AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
214 AC97_REG_CENTER_LFE_MASTER, 0x8080, 5, 8, 0, 0, CHECK_LFE
215 },
216 { AudioCoutputs, AudioNlfe, AudioNmute,
217 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
218 AC97_REG_CENTER_LFE_MASTER, 0x8080, 1, 15, 0, 0, CHECK_LFE
219 },
220 /* Tone */
221 { AudioCoutputs, Ac97Ntone, NULL,
222 AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
223 AC97_REG_MASTER_TONE, 0x0f0f, 4, 0, 0, 0, CHECK_TONE
224 },
225 /* PC Beep Volume */
226 { AudioCinputs, AudioNspeaker, NULL,
227 AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
228 AC97_REG_PCBEEP_VOLUME, 0x0000, 4, 1, 1,
229 },
230
231 /* Phone */
232 { AudioCinputs, Ac97Nphone, NULL,
233 AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
234 AC97_REG_PHONE_VOLUME, 0x8008, 5, 0, 1,
235 },
236 /* Mic Volume */
237 { AudioCinputs, AudioNmicrophone, NULL,
238 AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
239 AC97_REG_MIC_VOLUME, 0x8008, 5, 0, 1,
240 },
241 { AudioCinputs, AudioNmicrophone, AudioNpreamp,
242 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
243 AC97_REG_MIC_VOLUME, 0x8008, 1, 6, 0,
244 },
245 { AudioCinputs, AudioNmicrophone, AudioNsource,
246 AUDIO_MIXER_ENUM, WRAP(ac97_mic_select),
247 AC97_REG_GP, 0x0000, 1, 8, 0,
248 },
249 /* Line in Volume */
250 { AudioCinputs, AudioNline, NULL,
251 AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
252 AC97_REG_LINEIN_VOLUME, 0x8808, 5, 0, 1,
253 },
254 /* CD Volume */
255 { AudioCinputs, AudioNcd, NULL,
256 AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
257 AC97_REG_CD_VOLUME, 0x8808, 5, 0, 1,
258 },
259 /* Video Volume */
260 { AudioCinputs, AudioNvideo, NULL,
261 AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
262 AC97_REG_VIDEO_VOLUME, 0x8808, 5, 0, 1,
263 },
264 /* AUX volume */
265 { AudioCinputs, AudioNaux, NULL,
266 AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
267 AC97_REG_AUX_VOLUME, 0x8808, 5, 0, 1,
268 },
269 /* PCM out volume */
270 { AudioCinputs, AudioNdac, NULL,
271 AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
272 AC97_REG_PCMOUT_VOLUME, 0x8808, 5, 0, 1,
273 },
274 /* Record Source - some logic for this is hard coded - see below */
275 { AudioCrecord, AudioNsource, NULL,
276 AUDIO_MIXER_ENUM, WRAP(ac97_source),
277 AC97_REG_RECORD_SELECT, 0x0000, 3, 0, 0,
278 },
279 /* Record Gain */
280 { AudioCrecord, AudioNvolume, NULL,
281 AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
282 AC97_REG_RECORD_GAIN, 0x8000, 4, 0, 1, 1,
283 },
284 /* Record Gain mic */
285 { AudioCrecord, AudioNmicrophone, NULL,
286 AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
287 AC97_REG_RECORD_GAIN_MIC, 0x8000, 4, 0, 1, 1, CHECK_MIC
288 },
289 /* */
290 { AudioCoutputs, AudioNloudness, NULL,
291 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
292 AC97_REG_GP, 0x0000, 1, 12, 0, 0, CHECK_LOUDNESS
293 },
294 { AudioCoutputs, AudioNspatial, NULL,
295 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
296 AC97_REG_GP, 0x0000, 1, 13, 0, 1, CHECK_3D
297 },
298 { AudioCoutputs, AudioNspatial, "center",
299 AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
300 AC97_REG_3D_CONTROL, 0x0000, 4, 8, 0, 1, CHECK_3D
301 },
302 { AudioCoutputs, AudioNspatial, "depth",
303 AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
304 AC97_REG_3D_CONTROL, 0x0000, 4, 0, 0, 1, CHECK_3D
305 },
306
307 /* Missing features: Simulated Stereo, POP, Loopback mode */
308 } ;
309
310 #define SOURCE_INFO_SIZE (sizeof(source_info)/sizeof(source_info[0]))
311
312 /*
313 * Check out http://developer.intel.com/pc-supp/platform/ac97/ for
314 * information on AC-97
315 */
316
317 struct ac97_softc {
318 /* ac97_codec_if must be at the first of ac97_softc. */
319 struct ac97_codec_if codec_if;
320
321 struct ac97_host_if *host_if;
322
323 #define MAX_SOURCES (2 * SOURCE_INFO_SIZE)
324 struct ac97_source_info source_info[MAX_SOURCES];
325 int num_source_info;
326
327 enum ac97_host_flags host_flags;
328 unsigned int ac97_clock; /* usually 48000 */
329 #define AC97_STANDARD_CLOCK 48000U
330 u_int16_t caps; /* -> AC97_REG_RESET */
331 u_int16_t ext_id; /* -> AC97_REG_EXT_AUDIO_ID */
332 u_int16_t shadow_reg[128];
333 };
334
335 struct ac97_codec_if_vtbl ac97civ = {
336 ac97_mixer_get_port,
337 ac97_mixer_set_port,
338 ac97_query_devinfo,
339 ac97_get_portnum_by_name,
340 ac97_restore_shadow,
341 ac97_get_extcaps,
342 ac97_set_rate,
343 ac97_set_clock,
344 };
345
346 static const struct ac97_codecid {
347 u_int32_t id;
348 u_int32_t mask;
349 const char *name;
350 void (*init)(struct ac97_softc *);
351 } ac97codecid[] = {
352 /*
353 * Analog Devices SoundMAX
354 * http://www.soundmax.com/products/information/codecs.html
355 * http://www.analog.com/productSelection/pdf/AD1881A_0.pdf
356 * http://www.analog.com/productSelection/pdf/AD1885_0.pdf
357 * http://www.analog.com/UploadedFiles/Data_Sheets/206585810AD1980_0.pdf
358 * http://www.analog.com/productSelection/pdf/AD1981A_0.pdf
359 * http://www.analog.com/productSelection/pdf/AD1981B_0.pdf
360 * http://www.analog.com/UploadedFiles/Data_Sheets/180644528AD1985_0.pdf
361 */
362 { AC97_CODEC_ID('A', 'D', 'S', 3),
363 0xffffffff, "Analog Devices AD1819B" },
364 { AC97_CODEC_ID('A', 'D', 'S', 0x40),
365 0xffffffff, "Analog Devices AD1881" },
366 { AC97_CODEC_ID('A', 'D', 'S', 0x48),
367 0xffffffff, "Analog Devices AD1881A" },
368 { AC97_CODEC_ID('A', 'D', 'S', 0x60),
369 0xffffffff, "Analog Devices AD1885" },
370 { AC97_CODEC_ID('A', 'D', 'S', 0x61),
371 0xffffffff, "Analog Devices AD1886" },
372 { AC97_CODEC_ID('A', 'D', 'S', 0x63),
373 0xffffffff, "Analog Devices AD1886A" },
374 { AC97_CODEC_ID('A', 'D', 'S', 0x68),
375 0xffffffff, "Analog Devices AD1888", ac97_ad198x_init },
376 { AC97_CODEC_ID('A', 'D', 'S', 0x70),
377 0xffffffff, "Analog Devices AD1980", ac97_ad198x_init },
378 { AC97_CODEC_ID('A', 'D', 'S', 0x72),
379 0xffffffff, "Analog Devices AD1981A" },
380 { AC97_CODEC_ID('A', 'D', 'S', 0x74),
381 0xffffffff, "Analog Devices AD1981B" },
382 { AC97_CODEC_ID('A', 'D', 'S', 0x75),
383 0xffffffff, "Analog Devices AD1985", ac97_ad198x_init },
384 { AC97_CODEC_ID('A', 'D', 'S', 0),
385 AC97_VENDOR_ID_MASK, "Analog Devices unknown" },
386
387 /*
388 * Datasheets:
389 * http://www.asahi-kasei.co.jp/akm/usa/product/ak4541/ek4541.pdf
390 * http://www.asahi-kasei.co.jp/akm/usa/product/ak4543/ek4543.pdf
391 * http://www.asahi-kasei.co.jp/akm/usa/product/ak4544a/ek4544a.pdf
392 * http://www.asahi-kasei.co.jp/akm/usa/product/ak4545/ek4545.pdf
393 */
394 { AC97_CODEC_ID('A', 'K', 'M', 0),
395 0xffffffff, "Asahi Kasei AK4540" },
396 { AC97_CODEC_ID('A', 'K', 'M', 1),
397 0xffffffff, "Asahi Kasei AK4542" },
398 { AC97_CODEC_ID('A', 'K', 'M', 2),
399 0xffffffff, "Asahi Kasei AK4541/AK4543" },
400 { AC97_CODEC_ID('A', 'K', 'M', 5),
401 0xffffffff, "Asahi Kasei AK4544" },
402 { AC97_CODEC_ID('A', 'K', 'M', 6),
403 0xffffffff, "Asahi Kasei AK4544A" },
404 { AC97_CODEC_ID('A', 'K', 'M', 7),
405 0xffffffff, "Asahi Kasei AK4545" },
406 { AC97_CODEC_ID('A', 'K', 'M', 0),
407 AC97_VENDOR_ID_MASK, "Asahi Kasei unknown" },
408
409 /*
410 * Realtek & Avance Logic
411 * http://www.realtek.com.tw/downloads/downloads1-3.aspx?lineid=5&famid=All&series=All&Spec=True
412 *
413 * ALC650 and ALC658 support VRA, but it supports only 8000, 11025,
414 * 12000, 16000, 22050, 24000, 32000, 44100, and 48000 Hz.
415 */
416 { AC97_CODEC_ID('A', 'L', 'C', 0x00),
417 0xfffffff0, "Realtek RL5306" },
418 { AC97_CODEC_ID('A', 'L', 'C', 0x10),
419 0xfffffff0, "Realtek RL5382" },
420 { AC97_CODEC_ID('A', 'L', 'C', 0x20),
421 0xfffffff0, "Realtek RL5383/RL5522/ALC100" },
422 { AC97_CODEC_ID('A', 'L', 'G', 0x10),
423 0xffffffff, "Avance Logic ALC200/ALC201" },
424 { AC97_CODEC_ID('A', 'L', 'G', 0x20),
425 0xfffffff0, "Avance Logic ALC650", ac97_alc650_init },
426 { AC97_CODEC_ID('A', 'L', 'G', 0x30),
427 0xffffffff, "Avance Logic ALC101" },
428 { AC97_CODEC_ID('A', 'L', 'G', 0x40),
429 0xffffffff, "Avance Logic ALC202" },
430 { AC97_CODEC_ID('A', 'L', 'G', 0x50),
431 0xffffffff, "Avance Logic ALC250" },
432 { AC97_CODEC_ID('A', 'L', 'G', 0x60),
433 0xfffffff0, "Avance Logic ALC655" },
434 { AC97_CODEC_ID('A', 'L', 'G', 0x80),
435 0xfffffff0, "Avance Logic ALC658" },
436 { AC97_CODEC_ID('A', 'L', 'G', 0x90),
437 0xfffffff0, "Avance Logic ALC850" },
438 { AC97_CODEC_ID('A', 'L', 'C', 0),
439 AC97_VENDOR_ID_MASK, "Realtek unknown" },
440 { AC97_CODEC_ID('A', 'L', 'G', 0),
441 AC97_VENDOR_ID_MASK, "Avance Logic unknown" },
442
443 /**
444 * C-Media Electronics Inc.
445 * http://www.cmedia.com.tw/doc/CMI9739%206CH%20Audio%20Codec%20SPEC_Ver12.pdf
446 */
447 { AC97_CODEC_ID('C', 'M', 'I', 0x61),
448 0xffffffff, "C-Media CMI9739" },
449 { AC97_CODEC_ID('C', 'M', 'I', 0),
450 AC97_VENDOR_ID_MASK, "C-Media unknown" },
451
452 /* Cirrus Logic, Crystal series:
453 * 'C' 'R' 'Y' 0x0[0-7] - CS4297
454 * 0x1[0-7] - CS4297A
455 * 0x2[0-7] - CS4298
456 * 0x2[8-f] - CS4294
457 * 0x3[0-7] - CS4299
458 * 0x4[8-f] - CS4201
459 * 0x5[8-f] - CS4205
460 * 0x6[0-7] - CS4291
461 * 0x7[0-7] - CS4202
462 * Datasheets:
463 * http://www.cirrus.com/pubs/cs4297A-5.pdf?DocumentID=593
464 * http://www.cirrus.com/pubs/cs4294.pdf?DocumentID=32
465 * http://www.cirrus.com/pubs/cs4299-5.pdf?DocumentID=594
466 * http://www.cirrus.com/pubs/cs4201-2.pdf?DocumentID=492
467 * http://www.cirrus.com/pubs/cs4205-2.pdf?DocumentID=492
468 * http://www.cirrus.com/pubs/cs4202-1.pdf?DocumentID=852
469 */
470 { AC97_CODEC_ID('C', 'R', 'Y', 0x00),
471 0xfffffff8, "Crystal CS4297", },
472 { AC97_CODEC_ID('C', 'R', 'Y', 0x10),
473 0xfffffff8, "Crystal CS4297A", },
474 { AC97_CODEC_ID('C', 'R', 'Y', 0x20),
475 0xfffffff8, "Crystal CS4298", },
476 { AC97_CODEC_ID('C', 'R', 'Y', 0x28),
477 0xfffffff8, "Crystal CS4294", },
478 { AC97_CODEC_ID('C', 'R', 'Y', 0x30),
479 0xfffffff8, "Crystal CS4299", },
480 { AC97_CODEC_ID('C', 'R', 'Y', 0x48),
481 0xfffffff8, "Crystal CS4201", },
482 { AC97_CODEC_ID('C', 'R', 'Y', 0x58),
483 0xfffffff8, "Crystal CS4205", },
484 { AC97_CODEC_ID('C', 'R', 'Y', 0x60),
485 0xfffffff8, "Crystal CS4291", },
486 { AC97_CODEC_ID('C', 'R', 'Y', 0x70),
487 0xfffffff8, "Crystal CS4202", },
488 { AC97_CODEC_ID('C', 'R', 'Y', 0),
489 AC97_VENDOR_ID_MASK, "Cirrus Logic unknown", },
490
491 { 0x45838308, 0xffffffff, "ESS Technology ES1921", },
492 { 0x45838300, AC97_VENDOR_ID_MASK, "ESS Technology unknown", },
493
494 { AC97_CODEC_ID('H', 'R', 'S', 0),
495 0xffffffff, "Intersil HMP9701", },
496 { AC97_CODEC_ID('H', 'R', 'S', 0),
497 AC97_VENDOR_ID_MASK, "Intersil unknown", },
498
499 /*
500 * IC Ensemble (VIA)
501 * http://www.viatech.com/en/datasheet/DS1616.pdf
502 */
503 { AC97_CODEC_ID('I', 'C', 'E', 0x01),
504 0xffffffff, "ICEnsemble ICE1230/VT1611", },
505 { AC97_CODEC_ID('I', 'C', 'E', 0x11),
506 0xffffffff, "ICEnsemble ICE1232/VT1611A", },
507 { AC97_CODEC_ID('I', 'C', 'E', 0x14),
508 0xffffffff, "ICEnsemble ICE1232A", },
509 { AC97_CODEC_ID('I', 'C', 'E', 0x51),
510 0xffffffff, "VIA Technologies VT1616", ac97_vt1616_init },
511 { AC97_CODEC_ID('I', 'C', 'E', 0x52),
512 0xffffffff, "VIA Technologies VT1616i", ac97_vt1616_init },
513 { AC97_CODEC_ID('I', 'C', 'E', 0),
514 AC97_VENDOR_ID_MASK, "ICEnsemble/VIA unknown", },
515
516 { AC97_CODEC_ID('N', 'S', 'C', 0),
517 0xffffffff, "National Semiconductor LM454[03568]", },
518 { AC97_CODEC_ID('N', 'S', 'C', 49),
519 0xffffffff, "National Semiconductor LM4549", },
520 { AC97_CODEC_ID('N', 'S', 'C', 0),
521 AC97_VENDOR_ID_MASK, "National Semiconductor unknown", },
522
523 { AC97_CODEC_ID('P', 'S', 'C', 4),
524 0xffffffff, "Philips Semiconductor UCB1400", },
525 { AC97_CODEC_ID('P', 'S', 'C', 0),
526 AC97_VENDOR_ID_MASK, "Philips Semiconductor unknown", },
527
528 { AC97_CODEC_ID('S', 'I', 'L', 34),
529 0xffffffff, "Silicon Laboratory Si3036", },
530 { AC97_CODEC_ID('S', 'I', 'L', 35),
531 0xffffffff, "Silicon Laboratory Si3038", },
532 { AC97_CODEC_ID('S', 'I', 'L', 0),
533 AC97_VENDOR_ID_MASK, "Silicon Laboratory unknown", },
534
535 { AC97_CODEC_ID('T', 'R', 'A', 2),
536 0xffffffff, "TriTech TR28022", },
537 { AC97_CODEC_ID('T', 'R', 'A', 3),
538 0xffffffff, "TriTech TR28023", },
539 { AC97_CODEC_ID('T', 'R', 'A', 6),
540 0xffffffff, "TriTech TR28026", },
541 { AC97_CODEC_ID('T', 'R', 'A', 8),
542 0xffffffff, "TriTech TR28028", },
543 { AC97_CODEC_ID('T', 'R', 'A', 35),
544 0xffffffff, "TriTech TR28602", },
545 { AC97_CODEC_ID('T', 'R', 'A', 0),
546 AC97_VENDOR_ID_MASK, "TriTech unknown", },
547
548 { AC97_CODEC_ID('T', 'X', 'N', 0x20),
549 0xffffffff, "Texas Instruments TLC320AD9xC", },
550 { AC97_CODEC_ID('T', 'X', 'N', 0),
551 AC97_VENDOR_ID_MASK, "Texas Instruments unknown", },
552
553 /*
554 * VIA
555 * http://www.viatech.com/en/multimedia/audio.jsp
556 */
557 { AC97_CODEC_ID('V', 'I', 'A', 0x61),
558 0xffffffff, "VIA Technologies VT1612A", },
559 { AC97_CODEC_ID('V', 'I', 'A', 0),
560 AC97_VENDOR_ID_MASK, "VIA Technologies unknown", },
561
562 { AC97_CODEC_ID('W', 'E', 'C', 1),
563 0xffffffff, "Winbond W83971D", },
564 { AC97_CODEC_ID('W', 'E', 'C', 0),
565 AC97_VENDOR_ID_MASK, "Winbond unknown", },
566
567 /*
568 * http://www.wolfsonmicro.com/product_list.asp?cid=64
569 * http://www.wolfsonmicro.com/download.asp/did.56/WM9701A.pdf - 00
570 * http://www.wolfsonmicro.com/download.asp/did.57/WM9703.pdf - 03
571 * http://www.wolfsonmicro.com/download.asp/did.58/WM9704M.pdf - 04
572 * http://www.wolfsonmicro.com/download.asp/did.59/WM9704Q.pdf - 04
573 * http://www.wolfsonmicro.com/download.asp/did.184/WM9705_Rev34.pdf - 05
574 * http://www.wolfsonmicro.com/download.asp/did.60/WM9707.pdf - 03
575 * http://www.wolfsonmicro.com/download.asp/did.136/WM9708.pdf - 03
576 * http://www.wolfsonmicro.com/download.asp/did.243/WM9710.pdf - 05
577 */
578 { AC97_CODEC_ID('W', 'M', 'L', 0),
579 0xffffffff, "Wolfson WM9701A", },
580 { AC97_CODEC_ID('W', 'M', 'L', 3),
581 0xffffffff, "Wolfson WM9703/WM9707/WM9708", },
582 { AC97_CODEC_ID('W', 'M', 'L', 4),
583 0xffffffff, "Wolfson WM9704", },
584 { AC97_CODEC_ID('W', 'M', 'L', 5),
585 0xffffffff, "Wolfson WM9705/WM9710", },
586 { AC97_CODEC_ID('W', 'M', 'L', 0),
587 AC97_VENDOR_ID_MASK, "Wolfson unknown", },
588
589 /*
590 * http://www.yamaha.co.jp/english/product/lsi/us/products/pcaudio.html
591 * Datasheets:
592 * http://www.yamaha.co.jp/english/product/lsi/us/products/pdf/4MF743A20.pdf
593 * http://www.yamaha.co.jp/english/product/lsi/us/products/pdf/4MF753A20.pdf
594 */
595 { AC97_CODEC_ID('Y', 'M', 'H', 0),
596 0xffffffff, "Yamaha YMF743-S", },
597 { AC97_CODEC_ID('Y', 'M', 'H', 3),
598 0xffffffff, "Yamaha YMF753-S", },
599 { AC97_CODEC_ID('Y', 'M', 'H', 0),
600 AC97_VENDOR_ID_MASK, "Yamaha unknown", },
601
602 /*
603 * http://www.sigmatel.com/products/technical_docs.htm
604 * and
605 * http://www.sigmatel.com/documents/c-major-brochure-9-0.pdf
606 */
607 { 0x83847600, 0xffffffff, "SigmaTel STAC9700", },
608 { 0x83847604, 0xffffffff, "SigmaTel STAC9701/3/4/5", },
609 { 0x83847605, 0xffffffff, "SigmaTel STAC9704", },
610 { 0x83847608, 0xffffffff, "SigmaTel STAC9708", },
611 { 0x83847609, 0xffffffff, "SigmaTel STAC9721/23", },
612 { 0x83847644, 0xffffffff, "SigmaTel STAC9744/45", },
613 { 0x83847650, 0xffffffff, "SigmaTel STAC9750/51", },
614 { 0x83847656, 0xffffffff, "SigmaTel STAC9756/57", },
615 { 0x83847658, 0xffffffff, "SigmaTel STAC9758/59", },
616 { 0x83847666, 0xffffffff, "SigmaTel STAC9766/67", },
617 { 0x83847684, 0xffffffff, "SigmaTel STAC9783/84", },
618 { 0x83847600, AC97_VENDOR_ID_MASK, "SigmaTel unknown", },
619
620 { 0,
621 0, NULL, }
622 };
623
624 static const char * const ac97enhancement[] = {
625 "no 3D stereo",
626 "Analog Devices Phat Stereo",
627 "Creative",
628 "National Semi 3D",
629 "Yamaha Ymersion",
630 "BBE 3D",
631 "Crystal Semi 3D",
632 "Qsound QXpander",
633 "Spatializer 3D",
634 "SRS 3D",
635 "Platform Tech 3D",
636 "AKM 3D",
637 "Aureal",
638 "AZTECH 3D",
639 "Binaura 3D",
640 "ESS Technology",
641 "Harman International VMAx",
642 "Nvidea 3D",
643 "Philips Incredible Sound",
644 "Texas Instruments' 3D",
645 "VLSI Technology 3D",
646 "TriTech 3D",
647 "Realtek 3D",
648 "Samsung 3D",
649 "Wolfson Microelectronics 3D",
650 "Delta Integration 3D",
651 "SigmaTel 3D",
652 "KS Waves 3D",
653 "Rockwell 3D",
654 "Unknown 3D",
655 "Unknown 3D",
656 "Unknown 3D",
657 };
658
659 static const char * const ac97feature[] = {
660 "dedicated mic channel",
661 "reserved",
662 "tone",
663 "simulated stereo",
664 "headphone",
665 "bass boost",
666 "18 bit DAC",
667 "20 bit DAC",
668 "18 bit ADC",
669 "20 bit ADC"
670 };
671
672
673 /* #define AC97_DEBUG 10 */
674
675 #ifdef AUDIO_DEBUG
676 #define DPRINTF(x) if (ac97debug) printf x
677 #define DPRINTFN(n,x) if (ac97debug>(n)) printf x
678 #ifdef AC97_DEBUG
679 int ac97debug = AC97_DEBUG;
680 #else
681 int ac97debug = 0;
682 #endif
683 #else
684 #define DPRINTF(x)
685 #define DPRINTFN(n,x)
686 #endif
687
688 void
689 ac97_read(struct ac97_softc *as, u_int8_t reg, u_int16_t *val)
690 {
691 if (as->host_flags & AC97_HOST_DONT_READ &&
692 (reg != AC97_REG_VENDOR_ID1 && reg != AC97_REG_VENDOR_ID2 &&
693 reg != AC97_REG_RESET)) {
694 *val = as->shadow_reg[reg >> 1];
695 return;
696 }
697
698 if (as->host_if->read(as->host_if->arg, reg, val)) {
699 *val = as->shadow_reg[reg >> 1];
700 }
701 }
702
703 int
704 ac97_write(struct ac97_softc *as, u_int8_t reg, u_int16_t val)
705 {
706 as->shadow_reg[reg >> 1] = val;
707 return as->host_if->write(as->host_if->arg, reg, val);
708 }
709
710 void
711 ac97_setup_defaults(struct ac97_softc *as)
712 {
713 int idx;
714 const struct ac97_source_info *si;
715
716 memset(as->shadow_reg, 0, sizeof(as->shadow_reg));
717
718 for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
719 si = &source_info[idx];
720 ac97_write(as, si->reg, si->default_value);
721 }
722 }
723
724 void
725 ac97_restore_shadow(struct ac97_codec_if *self)
726 {
727 struct ac97_softc *as;
728 const struct ac97_source_info *si;
729 int idx;
730
731 as = (struct ac97_softc *) self;
732 for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
733 si = &source_info[idx];
734 ac97_write(as, si->reg, as->shadow_reg[si->reg >> 1]);
735 }
736
737 if (as->ext_id & (AC97_EXT_AUDIO_VRA | AC97_EXT_AUDIO_DRA
738 | AC97_EXT_AUDIO_SPDIF | AC97_EXT_AUDIO_VRM
739 | AC97_EXT_AUDIO_CDAC | AC97_EXT_AUDIO_SDAC
740 | AC97_EXT_AUDIO_LDAC)) {
741 ac97_write(as, AC97_REG_EXT_AUDIO_CTRL,
742 as->shadow_reg[AC97_REG_EXT_AUDIO_CTRL >> 1]);
743 }
744 }
745
746 int
747 ac97_str_equal(const char *a, const char *b)
748 {
749 return (a == b) || (a && b && (!strcmp(a, b)));
750 }
751
752 int
753 ac97_check_capability(struct ac97_softc *as, int check)
754 {
755 switch (check) {
756 case CHECK_NONE:
757 return 1;
758 case CHECK_SURROUND:
759 return as->ext_id & AC97_EXT_AUDIO_SDAC;
760 case CHECK_CENTER:
761 return as->ext_id & AC97_EXT_AUDIO_CDAC;
762 case CHECK_LFE:
763 return as->ext_id & AC97_EXT_AUDIO_LDAC;
764 case CHECK_HEADPHONES:
765 return as->caps & AC97_CAPS_HEADPHONES;
766 case CHECK_TONE:
767 return as->caps & AC97_CAPS_TONECTRL;
768 case CHECK_MIC:
769 return as->caps & AC97_CAPS_MICIN;
770 case CHECK_LOUDNESS:
771 return as->caps & AC97_CAPS_LOUDNESS;
772 case CHECK_3D:
773 return AC97_CAPS_ENHANCEMENT(as->caps) != 0;
774 default:
775 printf("%s: internal error: feature=%d\n", __func__, check);
776 return 0;
777 }
778 }
779
780 void
781 ac97_setup_source_info(struct ac97_softc *as)
782 {
783 int idx, ouridx;
784 struct ac97_source_info *si, *si2;
785
786 for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE; idx++) {
787 si = &as->source_info[ouridx];
788
789 if (!ac97_check_capability(as, source_info[idx].req_feature))
790 continue;
791
792 memcpy(si, &source_info[idx], sizeof(*si));
793
794 switch (si->type) {
795 case AUDIO_MIXER_CLASS:
796 si->mixer_class = ouridx;
797 ouridx++;
798 break;
799 case AUDIO_MIXER_VALUE:
800 /* Todo - Test to see if it works */
801 ouridx++;
802
803 /* Add an entry for mute, if necessary */
804 if (si->mute) {
805 si = &as->source_info[ouridx];
806 memcpy(si, &source_info[idx], sizeof(*si));
807 si->qualifier = AudioNmute;
808 si->type = AUDIO_MIXER_ENUM;
809 si->info = &ac97_on_off;
810 si->info_size = sizeof(ac97_on_off);
811 si->bits = 1;
812 si->ofs = 15;
813 si->mute = 0;
814 si->polarity = 0;
815 ouridx++;
816 }
817 break;
818 case AUDIO_MIXER_ENUM:
819 /* Todo - Test to see if it works */
820 ouridx++;
821 break;
822 default:
823 aprint_error ("ac97: shouldn't get here\n");
824 break;
825 }
826 }
827
828 as->num_source_info = ouridx;
829
830 for (idx = 0; idx < as->num_source_info; idx++) {
831 int idx2, previdx;
832
833 si = &as->source_info[idx];
834
835 /* Find mixer class */
836 for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
837 si2 = &as->source_info[idx2];
838
839 if (si2->type == AUDIO_MIXER_CLASS &&
840 ac97_str_equal(si->class,
841 si2->class)) {
842 si->mixer_class = idx2;
843 }
844 }
845
846
847 /* Setup prev and next pointers */
848 if (si->prev != 0)
849 continue;
850
851 if (si->qualifier)
852 continue;
853
854 si->prev = AUDIO_MIXER_LAST;
855 previdx = idx;
856
857 for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
858 if (idx2 == idx)
859 continue;
860
861 si2 = &as->source_info[idx2];
862
863 if (!si2->prev &&
864 ac97_str_equal(si->class, si2->class) &&
865 ac97_str_equal(si->device, si2->device)) {
866 as->source_info[previdx].next = idx2;
867 as->source_info[idx2].prev = previdx;
868
869 previdx = idx2;
870 }
871 }
872
873 as->source_info[previdx].next = AUDIO_MIXER_LAST;
874 }
875 }
876
877 int
878 ac97_attach(struct ac97_host_if *host_if)
879 {
880 struct ac97_softc *as;
881 struct device *sc_dev;
882 int error, i, j;
883 u_int32_t id;
884 u_int16_t id1, id2;
885 u_int16_t extstat, rate;
886 mixer_ctrl_t ctl;
887 void (*initfunc)(struct ac97_softc *);
888 #define FLAGBUFLEN 140
889 char flagbuf[FLAGBUFLEN];
890
891 sc_dev = (struct device *)host_if->arg;
892 initfunc = NULL;
893 as = malloc(sizeof(struct ac97_softc), M_DEVBUF, M_WAITOK|M_ZERO);
894
895 if (as == NULL)
896 return ENOMEM;
897
898 as->codec_if.vtbl = &ac97civ;
899 as->host_if = host_if;
900
901 if ((error = host_if->attach(host_if->arg, &as->codec_if))) {
902 free(as, M_DEVBUF);
903 return error;
904 }
905
906 host_if->reset(host_if->arg);
907
908 host_if->write(host_if->arg, AC97_REG_POWER, 0);
909 host_if->write(host_if->arg, AC97_REG_RESET, 0);
910
911 if (host_if->flags)
912 as->host_flags = host_if->flags(host_if->arg);
913
914 ac97_setup_defaults(as);
915 ac97_read(as, AC97_REG_RESET, &as->caps);
916 ac97_read(as, AC97_REG_VENDOR_ID1, &id1);
917 ac97_read(as, AC97_REG_VENDOR_ID2, &id2);
918
919 id = (id1 << 16) | id2;
920
921 aprint_normal("%s: ac97: ", sc_dev->dv_xname);
922
923 for (i = 0; ; i++) {
924 if (ac97codecid[i].id == 0) {
925 char pnp[4];
926
927 AC97_GET_CODEC_ID(id, pnp);
928 #define ISASCII(c) ((c) >= ' ' && (c) < 0x7f)
929 if (ISASCII(pnp[0]) && ISASCII(pnp[1]) &&
930 ISASCII(pnp[2]))
931 aprint_normal("%c%c%c%d",
932 pnp[0], pnp[1], pnp[2], pnp[3]);
933 else
934 aprint_normal("unknown (0x%08x)", id);
935 break;
936 }
937 if (ac97codecid[i].id == (id & ac97codecid[i].mask)) {
938 aprint_normal("%s", ac97codecid[i].name);
939 if (ac97codecid[i].mask == AC97_VENDOR_ID_MASK) {
940 aprint_normal(" (0x%08x)", id);
941 }
942 initfunc = ac97codecid[i].init;
943 break;
944 }
945 }
946 aprint_normal(" codec; ");
947 for (i = j = 0; i < 10; i++) {
948 if (as->caps & (1 << i)) {
949 aprint_normal("%s%s", j ? ", " : "", ac97feature[i]);
950 j++;
951 }
952 }
953 aprint_normal("%s%s\n", j ? ", " : "",
954 ac97enhancement[AC97_CAPS_ENHANCEMENT(as->caps)]);
955
956 as->ac97_clock = AC97_STANDARD_CLOCK;
957 ac97_read(as, AC97_REG_EXT_AUDIO_ID, &as->ext_id);
958 if (as->ext_id != 0) {
959 /* Print capabilities */
960 bitmask_snprintf(as->ext_id, "\20\20SECONDARY10\17SECONDARY01"
961 "\14AC97_23\13AC97_22\12AMAP\11LDAC\10SDAC"
962 "\7CDAC\4VRM\3SPDIF\2DRA\1VRA",
963 flagbuf, FLAGBUFLEN);
964 aprint_normal("%s: ac97: ext id %s\n", sc_dev->dv_xname, flagbuf);
965
966 /* Print unusual settings */
967 if (as->ext_id & AC97_EXT_AUDIO_DSA_MASK) {
968 aprint_normal("%s: ac97: Slot assignment: ",
969 sc_dev->dv_xname);
970 switch (as->ext_id & AC97_EXT_AUDIO_DSA_MASK) {
971 case AC97_EXT_AUDIO_DSA01:
972 aprint_normal("7&8, 6&9, 10&11.\n");
973 break;
974 case AC97_EXT_AUDIO_DSA10:
975 aprint_normal("6&9, 10&11, 3&4.\n");
976 break;
977 case AC97_EXT_AUDIO_DSA11:
978 aprint_normal("10&11, 3&4, 7&8.\n");
979 break;
980 }
981 }
982
983 /* Enable and disable features */
984 ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &extstat);
985 extstat &= ~AC97_EXT_AUDIO_DRA;
986 if (as->ext_id & AC97_EXT_AUDIO_LDAC)
987 extstat |= AC97_EXT_AUDIO_LDAC;
988 if (as->ext_id & AC97_EXT_AUDIO_SDAC)
989 extstat |= AC97_EXT_AUDIO_SDAC;
990 if (as->ext_id & AC97_EXT_AUDIO_CDAC)
991 extstat |= AC97_EXT_AUDIO_CDAC;
992 if (as->ext_id & AC97_EXT_AUDIO_VRM)
993 extstat |= AC97_EXT_AUDIO_VRM;
994 if (as->ext_id & AC97_EXT_AUDIO_SPDIF) {
995 /* Output the same data as DAC to SPDIF output */
996 extstat &= ~AC97_EXT_AUDIO_SPSA_MASK;
997 extstat |= AC97_EXT_AUDIO_SPSA34;
998 }
999 if (as->ext_id & AC97_EXT_AUDIO_VRA)
1000 extstat |= AC97_EXT_AUDIO_VRA;
1001 ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, extstat);
1002 if (as->ext_id & AC97_EXT_AUDIO_VRA) {
1003 /* VRA should be enabled. */
1004 /* so it claims to do variable rate, let's make sure */
1005 ac97_write(as, AC97_REG_PCM_FRONT_DAC_RATE, 44100);
1006 ac97_read(as, AC97_REG_PCM_FRONT_DAC_RATE, &rate);
1007 if (rate != 44100) {
1008 /* We can't believe ext_id */
1009 as->ext_id = 0;
1010 aprint_normal(
1011 "%s: Ignore these capabilities.\n",
1012 sc_dev->dv_xname);
1013 }
1014 /* restore the default value */
1015 ac97_write(as, AC97_REG_PCM_FRONT_DAC_RATE,
1016 AC97_SINGLE_RATE);
1017 }
1018 }
1019
1020 ac97_setup_source_info(as);
1021
1022 DELAY(900 * 1000);
1023 memset(&ctl, 0, sizeof(ctl));
1024
1025 /* disable mutes */
1026 for (i = 0; i < 11; i++) {
1027 static struct {
1028 char *class, *device;
1029 } d[11] = {
1030 { AudioCoutputs, AudioNmaster},
1031 { AudioCoutputs, AudioNheadphone},
1032 { AudioCoutputs, AudioNsurround},
1033 { AudioCoutputs, AudioNcenter},
1034 { AudioCoutputs, AudioNlfe},
1035 { AudioCinputs, AudioNdac},
1036 { AudioCinputs, AudioNcd},
1037 { AudioCinputs, AudioNline},
1038 { AudioCinputs, AudioNaux},
1039 { AudioCinputs, AudioNvideo},
1040 { AudioCrecord, AudioNvolume},
1041 };
1042
1043 ctl.type = AUDIO_MIXER_ENUM;
1044 ctl.un.ord = 0;
1045
1046 ctl.dev = ac97_get_portnum_by_name(&as->codec_if,
1047 d[i].class, d[i].device, AudioNmute);
1048 ac97_mixer_set_port(&as->codec_if, &ctl);
1049 }
1050 ctl.type = AUDIO_MIXER_ENUM;
1051 ctl.un.ord = 0;
1052 ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
1053 AudioNsource, NULL);
1054 ac97_mixer_set_port(&as->codec_if, &ctl);
1055
1056 /* set a reasonable default volume */
1057 ctl.type = AUDIO_MIXER_VALUE;
1058 ctl.un.value.num_channels = 2;
1059 ctl.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = \
1060 ctl.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 127;
1061 ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCoutputs,
1062 AudioNmaster, NULL);
1063 ac97_mixer_set_port(&as->codec_if, &ctl);
1064 ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCoutputs,
1065 AudioNsurround, NULL);
1066 ac97_mixer_set_port(&as->codec_if, &ctl);
1067 ctl.un.value.num_channels = 1;
1068 ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCoutputs,
1069 AudioNcenter, NULL);
1070 ac97_mixer_set_port(&as->codec_if, &ctl);
1071 ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCoutputs,
1072 AudioNlfe, NULL);
1073 ac97_mixer_set_port(&as->codec_if, &ctl);
1074
1075 if (initfunc != NULL)
1076 initfunc(as);
1077 return 0;
1078 }
1079
1080
1081 int
1082 ac97_query_devinfo(struct ac97_codec_if *codec_if, mixer_devinfo_t *dip)
1083 {
1084 struct ac97_softc *as;
1085 struct ac97_source_info *si;
1086 const char *name;
1087
1088 as = (struct ac97_softc *)codec_if;
1089 if (dip->index < as->num_source_info) {
1090 si = &as->source_info[dip->index];
1091 dip->type = si->type;
1092 dip->mixer_class = si->mixer_class;
1093 dip->prev = si->prev;
1094 dip->next = si->next;
1095
1096 if (si->qualifier)
1097 name = si->qualifier;
1098 else if (si->device)
1099 name = si->device;
1100 else if (si->class)
1101 name = si->class;
1102 else
1103 name = 0;
1104
1105 if (name)
1106 strcpy(dip->label.name, name);
1107
1108 memcpy(&dip->un, si->info, si->info_size);
1109
1110 /* Set the delta for volume sources */
1111 if (dip->type == AUDIO_MIXER_VALUE)
1112 dip->un.v.delta = 1 << (8 - si->bits);
1113
1114 return 0;
1115 }
1116
1117 return ENXIO;
1118 }
1119
1120
1121
1122 int
1123 ac97_mixer_set_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
1124 {
1125 struct ac97_softc *as;
1126 struct ac97_source_info *si;
1127 u_int16_t mask;
1128 u_int16_t val, newval;
1129 int error;
1130
1131 as = (struct ac97_softc *)codec_if;
1132 si = &as->source_info[cp->dev];
1133 if (cp->dev < 0 || cp->dev >= as->num_source_info)
1134 return EINVAL;
1135
1136 if (cp->type != si->type)
1137 return EINVAL;
1138
1139 ac97_read(as, si->reg, &val);
1140
1141 DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
1142
1143 mask = (1 << si->bits) - 1;
1144
1145 switch (cp->type) {
1146 case AUDIO_MIXER_ENUM:
1147 if (cp->un.ord > mask || cp->un.ord < 0)
1148 return EINVAL;
1149
1150 newval = (cp->un.ord << si->ofs);
1151 if (si->reg == AC97_REG_RECORD_SELECT) {
1152 newval |= (newval << (8 + si->ofs));
1153 mask |= (mask << 8);
1154 mask = mask << si->ofs;
1155 } else if (si->reg == AC97_REG_SURR_MASTER) {
1156 newval = cp->un.ord ? 0x8080 : 0x0000;
1157 mask = 0x8080;
1158 } else
1159 mask = mask << si->ofs;
1160 break;
1161 case AUDIO_MIXER_VALUE:
1162 {
1163 const struct audio_mixer_value *value = si->info;
1164 u_int16_t l, r, ol, or;
1165 int deltal, deltar;
1166
1167 if ((cp->un.value.num_channels <= 0) ||
1168 (cp->un.value.num_channels > value->num_channels))
1169 return EINVAL;
1170
1171 if (cp->un.value.num_channels == 1) {
1172 l = r = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
1173 } else {
1174 if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
1175 l = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
1176 r = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
1177 } else { /* left/right is reversed here */
1178 r = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
1179 l = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
1180 }
1181
1182 }
1183
1184 if (!si->polarity) {
1185 l = 255 - l;
1186 r = 255 - r;
1187 }
1188
1189 ol = (val >> (8+si->ofs)) & mask;
1190 or = (val >> si->ofs) & mask;
1191
1192 deltal = (ol << (8 - si->bits)) - l;
1193 deltar = (or << (8 - si->bits)) - r;
1194
1195 l = l >> (8 - si->bits);
1196 r = r >> (8 - si->bits);
1197
1198 if (deltal && ol == l)
1199 l += (deltal > 0) ? (l ? -1 : 0) : (l < mask ? 1 : 0);
1200 if (deltar && or == r)
1201 r += (deltar > 0) ? (r ? -1 : 0) : (r < mask ? 1 : 0);
1202
1203 newval = ((r & mask) << si->ofs);
1204 if (value->num_channels == 2) {
1205 newval = newval | ((l & mask) << (si->ofs+8));
1206 mask |= (mask << 8);
1207 }
1208 mask = mask << si->ofs;
1209 break;
1210 }
1211 default:
1212 return EINVAL;
1213 }
1214
1215 error = ac97_write(as, si->reg, (val & ~mask) | newval);
1216 if (error)
1217 return error;
1218
1219 return 0;
1220 }
1221
1222 int
1223 ac97_get_portnum_by_name(struct ac97_codec_if *codec_if, const char *class,
1224 const char *device, const char *qualifier)
1225 {
1226 struct ac97_softc *as;
1227 int idx;
1228
1229 as = (struct ac97_softc *)codec_if;
1230 for (idx = 0; idx < as->num_source_info; idx++) {
1231 struct ac97_source_info *si = &as->source_info[idx];
1232 if (ac97_str_equal(class, si->class) &&
1233 ac97_str_equal(device, si->device) &&
1234 ac97_str_equal(qualifier, si->qualifier))
1235 return idx;
1236 }
1237
1238 return -1;
1239 }
1240
1241 int
1242 ac97_mixer_get_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
1243 {
1244 struct ac97_softc *as;
1245 struct ac97_source_info *si;
1246 u_int16_t mask;
1247 u_int16_t val;
1248
1249 as = (struct ac97_softc *)codec_if;
1250 si = &as->source_info[cp->dev];
1251 if (cp->dev < 0 || cp->dev >= as->num_source_info)
1252 return EINVAL;
1253
1254 if (cp->type != si->type)
1255 return EINVAL;
1256
1257 ac97_read(as, si->reg, &val);
1258
1259 DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
1260
1261 mask = (1 << si->bits) - 1;
1262
1263 switch (cp->type) {
1264 case AUDIO_MIXER_ENUM:
1265 cp->un.ord = (val >> si->ofs) & mask;
1266 DPRINTFN(4, ("AUDIO_MIXER_ENUM: %x %d %x %d\n",
1267 val, si->ofs, mask, cp->un.ord));
1268 break;
1269 case AUDIO_MIXER_VALUE:
1270 {
1271 const struct audio_mixer_value *value = si->info;
1272 u_int16_t l, r;
1273
1274 if ((cp->un.value.num_channels <= 0) ||
1275 (cp->un.value.num_channels > value->num_channels))
1276 return EINVAL;
1277
1278 if (value->num_channels == 1) {
1279 l = r = (val >> si->ofs) & mask;
1280 } else {
1281 if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
1282 l = (val >> (si->ofs + 8)) & mask;
1283 r = (val >> si->ofs) & mask;
1284 } else { /* host has reversed channels */
1285 r = (val >> (si->ofs + 8)) & mask;
1286 l = (val >> si->ofs) & mask;
1287 }
1288 }
1289
1290 l = (l << (8 - si->bits));
1291 r = (r << (8 - si->bits));
1292 if (!si->polarity) {
1293 l = 255 - l;
1294 r = 255 - r;
1295 }
1296
1297 /* The EAP driver averages l and r for stereo
1298 channels that are requested in MONO mode. Does this
1299 make sense? */
1300 if (cp->un.value.num_channels == 1) {
1301 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = l;
1302 } else if (cp->un.value.num_channels == 2) {
1303 cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
1304 cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
1305 }
1306
1307 break;
1308 }
1309 default:
1310 return EINVAL;
1311 }
1312
1313 return 0;
1314 }
1315
1316
1317 int
1318 ac97_set_rate(struct ac97_codec_if *codec_if, int target, u_long *rate)
1319 {
1320 struct ac97_softc *as;
1321 u_long value;
1322 u_int16_t ext_stat;
1323 u_int16_t actual;
1324 u_int16_t power;
1325 u_int16_t power_bit;
1326
1327 as = (struct ac97_softc *)codec_if;
1328 if (target == AC97_REG_PCM_MIC_ADC_RATE) {
1329 if (!(as->ext_id & AC97_EXT_AUDIO_VRM)) {
1330 *rate = AC97_SINGLE_RATE;
1331 return 0;
1332 }
1333 } else {
1334 if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
1335 *rate = AC97_SINGLE_RATE;
1336 return 0;
1337 }
1338 }
1339 value = *rate * AC97_STANDARD_CLOCK / as->ac97_clock;
1340 ext_stat = 0;
1341 /*
1342 * PCM_FRONT_DAC_RATE/PCM_SURR_DAC_RATE/PCM_LFE_DAC_RATE
1343 * Check VRA, DRA
1344 * PCM_LR_ADC_RATE
1345 * Check VRA
1346 * PCM_MIC_ADC_RATE
1347 * Check VRM
1348 */
1349 switch (target) {
1350 case AC97_REG_PCM_FRONT_DAC_RATE:
1351 case AC97_REG_PCM_SURR_DAC_RATE:
1352 case AC97_REG_PCM_LFE_DAC_RATE:
1353 power_bit = AC97_POWER_OUT;
1354 if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
1355 *rate = AC97_SINGLE_RATE;
1356 return 0;
1357 }
1358 if (as->ext_id & AC97_EXT_AUDIO_DRA) {
1359 ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &ext_stat);
1360 if (value > 0x1ffff) {
1361 return EINVAL;
1362 } else if (value > 0xffff) {
1363 /* Enable DRA */
1364 ext_stat |= AC97_EXT_AUDIO_DRA;
1365 ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, ext_stat);
1366 value /= 2;
1367 } else {
1368 /* Disable DRA */
1369 ext_stat &= ~AC97_EXT_AUDIO_DRA;
1370 ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, ext_stat);
1371 }
1372 } else {
1373 if (value > 0xffff)
1374 return EINVAL;
1375 }
1376 break;
1377 case AC97_REG_PCM_LR_ADC_RATE:
1378 power_bit = AC97_POWER_IN;
1379 if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
1380 *rate = AC97_SINGLE_RATE;
1381 return 0;
1382 }
1383 if (value > 0xffff)
1384 return EINVAL;
1385 break;
1386 case AC97_REG_PCM_MIC_ADC_RATE:
1387 power_bit = AC97_POWER_IN;
1388 if (!(as->ext_id & AC97_EXT_AUDIO_VRM)) {
1389 *rate = AC97_SINGLE_RATE;
1390 return 0;
1391 }
1392 if (value > 0xffff)
1393 return EINVAL;
1394 break;
1395 default:
1396 printf("%s: Unknown register: 0x%x\n", __func__, target);
1397 return EINVAL;
1398 }
1399
1400 ac97_read(as, AC97_REG_POWER, &power);
1401 ac97_write(as, AC97_REG_POWER, power | power_bit);
1402
1403 ac97_write(as, target, (u_int16_t)value);
1404 ac97_read(as, target, &actual);
1405 actual = (u_int32_t)actual * as->ac97_clock / AC97_STANDARD_CLOCK;
1406
1407 ac97_write(as, AC97_REG_POWER, power);
1408 if (ext_stat & AC97_EXT_AUDIO_DRA) {
1409 *rate = actual * 2;
1410 } else {
1411 *rate = actual;
1412 }
1413 return 0;
1414 }
1415
1416 void
1417 ac97_set_clock(struct ac97_codec_if *codec_if, unsigned int clock)
1418 {
1419 struct ac97_softc *as;
1420
1421 as = (struct ac97_softc *)codec_if;
1422 as->ac97_clock = clock;
1423 }
1424
1425 u_int16_t
1426 ac97_get_extcaps(struct ac97_codec_if *codec_if)
1427 {
1428 struct ac97_softc *as;
1429
1430 as = (struct ac97_softc *)codec_if;
1431 return as->ext_id;
1432 }
1433
1434 int
1435 ac97_add_port(struct ac97_softc *as, const struct ac97_source_info *src)
1436 {
1437 struct ac97_source_info *si;
1438 int ouridx, idx;
1439
1440 if (as->num_source_info >= MAX_SOURCES) {
1441 printf("%s: internal error: increase MAX_SOURCES in %s\n",
1442 __func__, __FILE__);
1443 return -1;
1444 }
1445 if (!ac97_check_capability(as, src->req_feature))
1446 return -1;
1447 ouridx = as->num_source_info;
1448 si = &as->source_info[ouridx];
1449 memcpy(si, src, sizeof(*si));
1450
1451 switch (si->type) {
1452 case AUDIO_MIXER_CLASS:
1453 case AUDIO_MIXER_VALUE:
1454 printf("%s: adding class/value is not supported yet.\n",
1455 __func__);
1456 return -1;
1457 case AUDIO_MIXER_ENUM:
1458 break;
1459 default:
1460 printf("%s: unknown type: %d\n", __func__, si->type);
1461 return -1;
1462 }
1463 as->num_source_info++;
1464
1465 si->mixer_class = ac97_get_portnum_by_name(&as->codec_if, si->class,
1466 NULL, NULL);
1467 /* Find the root of the device */
1468 idx = ac97_get_portnum_by_name(&as->codec_if, si->class,
1469 si->device, NULL);
1470 /* Find the last item */
1471 while (as->source_info[idx].next != AUDIO_MIXER_LAST)
1472 idx = as->source_info[idx].next;
1473 /* Append */
1474 as->source_info[idx].next = ouridx;
1475 si->prev = idx;
1476 si->next = AUDIO_MIXER_LAST;
1477
1478 return 0;
1479 }
1480
1481 /**
1482 * Codec-dependent initialization
1483 */
1484
1485 #define AD1980_REG_MISC 0x76
1486 #define AD1980_MISC_MBG0 0x0001 /* 0 1888/1980/1981 /1985 */
1487 #define AD1980_MISC_MBG1 0x0002 /* 1 1888/1980/1981 /1985 */
1488 #define AD1980_MISC_VREFD 0x0004 /* 2 1888/1980/1981 /1985 */
1489 #define AD1980_MISC_VREFH 0x0008 /* 3 1888/1980/1981 /1985 */
1490 #define AD1980_MISC_SRU 0x0010 /* 4 1888/1980 /1985 */
1491 #define AD1980_MISC_LOSEL 0x0020 /* 5 1888/1980/1981 /1985 */
1492 #define AD1980_MISC_2CMIC 0x0040 /* 6 1980/1981B/1985 */
1493 #define AD1980_MISC_SPRD 0x0080 /* 7 1888/1980 /1985 */
1494 #define AD1980_MISC_DMIX0 0x0100 /* 8 1888/1980 /1985 */
1495 #define AD1980_MISC_DMIX1 0x0200 /* 9 1888/1980 /1985 */
1496 #define AD1980_MISC_HPSEL 0x0400 /*10 1888/1980 /1985 */
1497 #define AD1980_MISC_CLDIS 0x0800 /*11 1888/1980 /1985 */
1498 #define AD1980_MISC_LODIS 0x1000 /*12 1888/1980/1981 /1985 */
1499 #define AD1980_MISC_MSPLT 0x2000 /*13 1888/1980/1981 /1985 */
1500 #define AD1980_MISC_AC97NC 0x4000 /*14 1888/1980 /1985 */
1501 #define AD1980_MISC_DACZ 0x8000 /*15 1888/1980/1981 /1985 */
1502 #define AD1981_REG_MISC 0x76
1503 #define AD1981_MISC_MADST 0x0010 /* 4 */
1504 #define AD1981A_MISC_MADPD 0x0040 /* 6 */
1505 #define AD1981B_MISC_MADPD 0x0080 /* 7 */
1506 #define AD1981_MISC_FMXE 0x0200 /* 9 */
1507 #define AD1981_MISC_DAM 0x0800 /*11 */
1508 static void
1509 ac97_ad198x_init(struct ac97_softc *as)
1510 {
1511 int i;
1512 uint16_t misc;
1513
1514 ac97_read(as, AD1980_REG_MISC, &misc);
1515 ac97_write(as, AD1980_REG_MISC,
1516 misc | AD1980_MISC_LOSEL | AD1980_MISC_HPSEL);
1517
1518 for (i = 0; i < as->num_source_info; i++) {
1519 if (as->source_info[i].type != AUDIO_MIXER_VALUE)
1520 continue;
1521
1522 if (as->source_info[i].reg == AC97_REG_MASTER_VOLUME)
1523 as->source_info[i].reg = AC97_REG_SURR_MASTER;
1524 else if (as->source_info[i].reg == AC97_REG_SURR_MASTER)
1525 as->source_info[i].reg = AC97_REG_MASTER_VOLUME;
1526 }
1527 }
1528
1529 #define ALC650_REG_MULTI_CHANNEL_CONTROL 0x6a
1530 #define ALC650_MCC_SLOT_MODIFY_MASK 0xc000
1531 #define ALC650_MCC_FRONTDAC_FROM_SPDIFIN 0x2000 /* 13 */
1532 #define ALC650_MCC_SPDIFOUT_FROM_ADC 0x1000 /* 12 */
1533 #define ALC650_MCC_PCM_FROM_SPDIFIN 0x0800 /* 11 */
1534 #define ALC650_MCC_MIC_OR_CENTERLFE 0x0400 /* 10 */
1535 #define ALC650_MCC_LINEIN_OR_SURROUND 0x0200 /* 9 */
1536 #define ALC650_MCC_INDEPENDENT_MASTER_L 0x0080 /* 7 */
1537 #define ALC650_MCC_INDEPENDENT_MASTER_R 0x0040 /* 6 */
1538 #define ALC650_MCC_ANALOG_TO_CENTERLFE 0x0020 /* 5 */
1539 #define ALC650_MCC_ANALOG_TO_SURROUND 0x0010 /* 4 */
1540 #define ALC650_MCC_EXCHANGE_CENTERLFE 0x0008 /* 3 */
1541 #define ALC650_MCC_CENTERLFE_DOWNMIX 0x0004 /* 2 */
1542 #define ALC650_MCC_SURROUND_DOWNMIX 0x0002 /* 1 */
1543 #define ALC650_MCC_LINEOUT_TO_SURROUND 0x0001 /* 0 */
1544 static void
1545 ac97_alc650_init(struct ac97_softc *as)
1546 {
1547 static const struct ac97_source_info sources[6] = {
1548 { AudioCoutputs, AudioNsurround, "lineinjack",
1549 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1550 ALC650_REG_MULTI_CHANNEL_CONTROL,
1551 0x0000, 1, 9, 0, 0, CHECK_SURROUND },
1552 { AudioCoutputs, AudioNsurround, "mixtofront",
1553 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1554 ALC650_REG_MULTI_CHANNEL_CONTROL,
1555 0x0000, 1, 1, 0, 0, CHECK_SURROUND },
1556 { AudioCoutputs, AudioNcenter, "micjack",
1557 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1558 ALC650_REG_MULTI_CHANNEL_CONTROL,
1559 0x0000, 1, 10, 0, 0, CHECK_CENTER },
1560 { AudioCoutputs, AudioNlfe, "micjack",
1561 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1562 ALC650_REG_MULTI_CHANNEL_CONTROL,
1563 0x0000, 1, 10, 0, 0, CHECK_LFE },
1564 { AudioCoutputs, AudioNcenter, "mixtofront",
1565 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1566 ALC650_REG_MULTI_CHANNEL_CONTROL,
1567 0x0000, 1, 2, 0, 0, CHECK_CENTER },
1568 { AudioCoutputs, AudioNlfe, "mixtofront",
1569 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1570 ALC650_REG_MULTI_CHANNEL_CONTROL,
1571 0x0000, 1, 2, 0, 0, CHECK_LFE },
1572 };
1573
1574 ac97_add_port(as, &sources[0]);
1575 ac97_add_port(as, &sources[1]);
1576 ac97_add_port(as, &sources[2]);
1577 ac97_add_port(as, &sources[3]);
1578 ac97_add_port(as, &sources[4]);
1579 ac97_add_port(as, &sources[5]);
1580 }
1581
1582 #define VT1616_REG_IO_CONTROL 0x5a
1583 #define VT1616_IC_LVL (1 << 15)
1584 #define VT1616_IC_LFECENTER_TO_FRONT (1 << 12)
1585 #define VT1616_IC_SURROUND_TO_FRONT (1 << 11)
1586 #define VT1616_IC_BPDC (1 << 10)
1587 #define VT1616_IC_DC (1 << 9)
1588 #define VT1616_IC_IB_MASK 0x000c
1589 static void
1590 ac97_vt1616_init(struct ac97_softc *as)
1591 {
1592 static const struct ac97_source_info sources[3] = {
1593 { AudioCoutputs, AudioNsurround, "mixtofront",
1594 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1595 VT1616_REG_IO_CONTROL,
1596 0x0000, 1, 11, 0, 0, CHECK_SURROUND },
1597 { AudioCoutputs, AudioNcenter, "mixtofront",
1598 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1599 VT1616_REG_IO_CONTROL,
1600 0x0000, 1, 12, 0, 0, CHECK_CENTER },
1601 { AudioCoutputs, AudioNlfe, "mixtofront",
1602 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1603 VT1616_REG_IO_CONTROL,
1604 0x0000, 1, 12, 0, 0, CHECK_LFE },
1605 };
1606
1607 ac97_add_port(as, &sources[0]);
1608 ac97_add_port(as, &sources[1]);
1609 ac97_add_port(as, &sources[2]);
1610 }
1611