ac97.c revision 1.56 1 /* $NetBSD: ac97.c,v 1.56 2004/08/08 03:50:56 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.56 2004/08/08 03:50:56 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 AC97_VENDOR_ID_MASK, "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', 0),
512 AC97_VENDOR_ID_MASK, "ICEnsemble unknown", },
513
514 { AC97_CODEC_ID('N', 'S', 'C', 0),
515 0xffffffff, "National Semiconductor LM454[03568]", },
516 { AC97_CODEC_ID('N', 'S', 'C', 49),
517 0xffffffff, "National Semiconductor LM4549", },
518 { AC97_CODEC_ID('N', 'S', 'C', 0),
519 AC97_VENDOR_ID_MASK, "National Semiconductor unknown", },
520
521 { AC97_CODEC_ID('P', 'S', 'C', 4),
522 0xffffffff, "Philips Semiconductor UCB1400", },
523 { AC97_CODEC_ID('P', 'S', 'C', 0),
524 AC97_VENDOR_ID_MASK, "Philips Semiconductor unknown", },
525
526 { AC97_CODEC_ID('S', 'I', 'L', 34),
527 0xffffffff, "Silicon Laboratory Si3036", },
528 { AC97_CODEC_ID('S', 'I', 'L', 35),
529 0xffffffff, "Silicon Laboratory Si3038", },
530 { AC97_CODEC_ID('S', 'I', 'L', 0),
531 AC97_VENDOR_ID_MASK, "Silicon Laboratory unknown", },
532
533 { AC97_CODEC_ID('T', 'R', 'A', 2),
534 0xffffffff, "TriTech TR28022", },
535 { AC97_CODEC_ID('T', 'R', 'A', 3),
536 0xffffffff, "TriTech TR28023", },
537 { AC97_CODEC_ID('T', 'R', 'A', 6),
538 0xffffffff, "TriTech TR28026", },
539 { AC97_CODEC_ID('T', 'R', 'A', 8),
540 0xffffffff, "TriTech TR28028", },
541 { AC97_CODEC_ID('T', 'R', 'A', 35),
542 0xffffffff, "TriTech TR28602", },
543 { AC97_CODEC_ID('T', 'R', 'A', 0),
544 AC97_VENDOR_ID_MASK, "TriTech unknown", },
545
546 { AC97_CODEC_ID('T', 'X', 'N', 0x20),
547 0xffffffff, "Texas Instruments TLC320AD9xC", },
548 { AC97_CODEC_ID('T', 'X', 'N', 0),
549 AC97_VENDOR_ID_MASK, "Texas Instruments unknown", },
550
551 /*
552 * VIA
553 * http://www.viatech.com/en/multimedia/audio.jsp
554 */
555 { AC97_CODEC_ID('V', 'I', 'A', 0x61),
556 0xffffffff, "VIA Technologies VT1612A", },
557 { AC97_CODEC_ID('V', 'I', 'A', 0),
558 AC97_VENDOR_ID_MASK, "VIA Technologies unknown", },
559
560 { AC97_CODEC_ID('W', 'E', 'C', 1),
561 0xffffffff, "Winbond W83971D", },
562 { AC97_CODEC_ID('W', 'E', 'C', 0),
563 AC97_VENDOR_ID_MASK, "Winbond unknown", },
564
565 /*
566 * http://www.wolfsonmicro.com/product_list.asp?cid=64
567 * http://www.wolfsonmicro.com/download.asp/did.56/WM9701A.pdf - 00
568 * http://www.wolfsonmicro.com/download.asp/did.57/WM9703.pdf - 03
569 * http://www.wolfsonmicro.com/download.asp/did.58/WM9704M.pdf - 04
570 * http://www.wolfsonmicro.com/download.asp/did.59/WM9704Q.pdf - 04
571 * http://www.wolfsonmicro.com/download.asp/did.184/WM9705_Rev34.pdf - 05
572 * http://www.wolfsonmicro.com/download.asp/did.60/WM9707.pdf - 03
573 * http://www.wolfsonmicro.com/download.asp/did.136/WM9708.pdf - 03
574 * http://www.wolfsonmicro.com/download.asp/did.243/WM9710.pdf - 05
575 */
576 { AC97_CODEC_ID('W', 'M', 'L', 0),
577 0xffffffff, "Wolfson WM9701A", },
578 { AC97_CODEC_ID('W', 'M', 'L', 3),
579 0xffffffff, "Wolfson WM9703/WM9707/WM9708", },
580 { AC97_CODEC_ID('W', 'M', 'L', 4),
581 0xffffffff, "Wolfson WM9704", },
582 { AC97_CODEC_ID('W', 'M', 'L', 5),
583 0xffffffff, "Wolfson WM9705/WM9710", },
584 { AC97_CODEC_ID('W', 'M', 'L', 0),
585 AC97_VENDOR_ID_MASK, "Wolfson unknown", },
586
587 /*
588 * http://www.yamaha.co.jp/english/product/lsi/us/products/pcaudio.html
589 * Datasheets:
590 * http://www.yamaha.co.jp/english/product/lsi/us/products/pdf/4MF743A20.pdf
591 * http://www.yamaha.co.jp/english/product/lsi/us/products/pdf/4MF753A20.pdf
592 */
593 { AC97_CODEC_ID('Y', 'M', 'H', 0),
594 0xffffffff, "Yamaha YMF743-S", },
595 { AC97_CODEC_ID('Y', 'M', 'H', 3),
596 0xffffffff, "Yamaha YMF753-S", },
597 { AC97_CODEC_ID('Y', 'M', 'H', 0),
598 AC97_VENDOR_ID_MASK, "Yamaha unknown", },
599
600 /*
601 * http://www.sigmatel.com/products/technical_docs.htm
602 * and
603 * http://www.sigmatel.com/documents/c-major-brochure-9-0.pdf
604 */
605 { 0x83847600, 0xffffffff, "SigmaTel STAC9700", },
606 { 0x83847604, 0xffffffff, "SigmaTel STAC9701/3/4/5", },
607 { 0x83847605, 0xffffffff, "SigmaTel STAC9704", },
608 { 0x83847608, 0xffffffff, "SigmaTel STAC9708", },
609 { 0x83847609, 0xffffffff, "SigmaTel STAC9721/23", },
610 { 0x83847644, 0xffffffff, "SigmaTel STAC9744/45", },
611 { 0x83847650, 0xffffffff, "SigmaTel STAC9750/51", },
612 { 0x83847656, 0xffffffff, "SigmaTel STAC9756/57", },
613 { 0x83847658, 0xffffffff, "SigmaTel STAC9758/59", },
614 { 0x83847666, 0xffffffff, "SigmaTel STAC9766/67", },
615 { 0x83847684, 0xffffffff, "SigmaTel STAC9783/84", },
616 { 0x83847600, AC97_VENDOR_ID_MASK, "SigmaTel unknown", },
617
618 { 0,
619 0, NULL, }
620 };
621
622 static const char * const ac97enhancement[] = {
623 "no 3D stereo",
624 "Analog Devices Phat Stereo",
625 "Creative",
626 "National Semi 3D",
627 "Yamaha Ymersion",
628 "BBE 3D",
629 "Crystal Semi 3D",
630 "Qsound QXpander",
631 "Spatializer 3D",
632 "SRS 3D",
633 "Platform Tech 3D",
634 "AKM 3D",
635 "Aureal",
636 "AZTECH 3D",
637 "Binaura 3D",
638 "ESS Technology",
639 "Harman International VMAx",
640 "Nvidea 3D",
641 "Philips Incredible Sound",
642 "Texas Instruments' 3D",
643 "VLSI Technology 3D",
644 "TriTech 3D",
645 "Realtek 3D",
646 "Samsung 3D",
647 "Wolfson Microelectronics 3D",
648 "Delta Integration 3D",
649 "SigmaTel 3D",
650 "KS Waves 3D",
651 "Rockwell 3D",
652 "Unknown 3D",
653 "Unknown 3D",
654 "Unknown 3D",
655 };
656
657 static const char * const ac97feature[] = {
658 "dedicated mic channel",
659 "reserved",
660 "tone",
661 "simulated stereo",
662 "headphone",
663 "bass boost",
664 "18 bit DAC",
665 "20 bit DAC",
666 "18 bit ADC",
667 "20 bit ADC"
668 };
669
670
671 /* #define AC97_DEBUG 10 */
672
673 #ifdef AUDIO_DEBUG
674 #define DPRINTF(x) if (ac97debug) printf x
675 #define DPRINTFN(n,x) if (ac97debug>(n)) printf x
676 #ifdef AC97_DEBUG
677 int ac97debug = AC97_DEBUG;
678 #else
679 int ac97debug = 0;
680 #endif
681 #else
682 #define DPRINTF(x)
683 #define DPRINTFN(n,x)
684 #endif
685
686 void
687 ac97_read(struct ac97_softc *as, u_int8_t reg, u_int16_t *val)
688 {
689 if (as->host_flags & AC97_HOST_DONT_READ &&
690 (reg != AC97_REG_VENDOR_ID1 && reg != AC97_REG_VENDOR_ID2 &&
691 reg != AC97_REG_RESET)) {
692 *val = as->shadow_reg[reg >> 1];
693 return;
694 }
695
696 if (as->host_if->read(as->host_if->arg, reg, val)) {
697 *val = as->shadow_reg[reg >> 1];
698 }
699 }
700
701 int
702 ac97_write(struct ac97_softc *as, u_int8_t reg, u_int16_t val)
703 {
704 as->shadow_reg[reg >> 1] = val;
705 return as->host_if->write(as->host_if->arg, reg, val);
706 }
707
708 void
709 ac97_setup_defaults(struct ac97_softc *as)
710 {
711 int idx;
712 const struct ac97_source_info *si;
713
714 memset(as->shadow_reg, 0, sizeof(as->shadow_reg));
715
716 for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
717 si = &source_info[idx];
718 ac97_write(as, si->reg, si->default_value);
719 }
720 }
721
722 void
723 ac97_restore_shadow(struct ac97_codec_if *self)
724 {
725 struct ac97_softc *as;
726 const struct ac97_source_info *si;
727 int idx;
728
729 as = (struct ac97_softc *) self;
730 for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
731 si = &source_info[idx];
732 ac97_write(as, si->reg, as->shadow_reg[si->reg >> 1]);
733 }
734
735 if (as->ext_id & (AC97_EXT_AUDIO_VRA | AC97_EXT_AUDIO_DRA
736 | AC97_EXT_AUDIO_SPDIF | AC97_EXT_AUDIO_VRM
737 | AC97_EXT_AUDIO_CDAC | AC97_EXT_AUDIO_SDAC
738 | AC97_EXT_AUDIO_LDAC)) {
739 ac97_write(as, AC97_REG_EXT_AUDIO_CTRL,
740 as->shadow_reg[AC97_REG_EXT_AUDIO_CTRL >> 1]);
741 }
742 }
743
744 int
745 ac97_str_equal(const char *a, const char *b)
746 {
747 return (a == b) || (a && b && (!strcmp(a, b)));
748 }
749
750 int
751 ac97_check_capability(struct ac97_softc *as, int check)
752 {
753 switch (check) {
754 case CHECK_NONE:
755 return 1;
756 case CHECK_SURROUND:
757 return as->ext_id & AC97_EXT_AUDIO_SDAC;
758 case CHECK_CENTER:
759 return as->ext_id & AC97_EXT_AUDIO_CDAC;
760 case CHECK_LFE:
761 return as->ext_id & AC97_EXT_AUDIO_LDAC;
762 case CHECK_HEADPHONES:
763 return as->caps & AC97_CAPS_HEADPHONES;
764 case CHECK_TONE:
765 return as->caps & AC97_CAPS_TONECTRL;
766 case CHECK_MIC:
767 return as->caps & AC97_CAPS_MICIN;
768 case CHECK_LOUDNESS:
769 return as->caps & AC97_CAPS_LOUDNESS;
770 case CHECK_3D:
771 return AC97_CAPS_ENHANCEMENT(as->caps) != 0;
772 default:
773 printf("%s: internal error: feature=%d\n", __func__, check);
774 return 0;
775 }
776 }
777
778 void
779 ac97_setup_source_info(struct ac97_softc *as)
780 {
781 int idx, ouridx;
782 struct ac97_source_info *si, *si2;
783
784 for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE; idx++) {
785 si = &as->source_info[ouridx];
786
787 if (!ac97_check_capability(as, source_info[idx].req_feature))
788 continue;
789
790 memcpy(si, &source_info[idx], sizeof(*si));
791
792 switch (si->type) {
793 case AUDIO_MIXER_CLASS:
794 si->mixer_class = ouridx;
795 ouridx++;
796 break;
797 case AUDIO_MIXER_VALUE:
798 /* Todo - Test to see if it works */
799 ouridx++;
800
801 /* Add an entry for mute, if necessary */
802 if (si->mute) {
803 si = &as->source_info[ouridx];
804 memcpy(si, &source_info[idx], sizeof(*si));
805 si->qualifier = AudioNmute;
806 si->type = AUDIO_MIXER_ENUM;
807 si->info = &ac97_on_off;
808 si->info_size = sizeof(ac97_on_off);
809 si->bits = 1;
810 si->ofs = 15;
811 si->mute = 0;
812 si->polarity = 0;
813 ouridx++;
814 }
815 break;
816 case AUDIO_MIXER_ENUM:
817 /* Todo - Test to see if it works */
818 ouridx++;
819 break;
820 default:
821 aprint_error ("ac97: shouldn't get here\n");
822 break;
823 }
824 }
825
826 as->num_source_info = ouridx;
827
828 for (idx = 0; idx < as->num_source_info; idx++) {
829 int idx2, previdx;
830
831 si = &as->source_info[idx];
832
833 /* Find mixer class */
834 for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
835 si2 = &as->source_info[idx2];
836
837 if (si2->type == AUDIO_MIXER_CLASS &&
838 ac97_str_equal(si->class,
839 si2->class)) {
840 si->mixer_class = idx2;
841 }
842 }
843
844
845 /* Setup prev and next pointers */
846 if (si->prev != 0)
847 continue;
848
849 if (si->qualifier)
850 continue;
851
852 si->prev = AUDIO_MIXER_LAST;
853 previdx = idx;
854
855 for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
856 if (idx2 == idx)
857 continue;
858
859 si2 = &as->source_info[idx2];
860
861 if (!si2->prev &&
862 ac97_str_equal(si->class, si2->class) &&
863 ac97_str_equal(si->device, si2->device)) {
864 as->source_info[previdx].next = idx2;
865 as->source_info[idx2].prev = previdx;
866
867 previdx = idx2;
868 }
869 }
870
871 as->source_info[previdx].next = AUDIO_MIXER_LAST;
872 }
873 }
874
875 int
876 ac97_attach(struct ac97_host_if *host_if)
877 {
878 struct ac97_softc *as;
879 struct device *sc_dev;
880 int error, i, j;
881 u_int32_t id;
882 u_int16_t id1, id2;
883 u_int16_t extstat, rate;
884 mixer_ctrl_t ctl;
885 void (*initfunc)(struct ac97_softc *);
886 #define FLAGBUFLEN 140
887 char flagbuf[FLAGBUFLEN];
888
889 sc_dev = (struct device *)host_if->arg;
890 initfunc = NULL;
891 as = malloc(sizeof(struct ac97_softc), M_DEVBUF, M_WAITOK|M_ZERO);
892
893 if (as == NULL)
894 return ENOMEM;
895
896 as->codec_if.vtbl = &ac97civ;
897 as->host_if = host_if;
898
899 if ((error = host_if->attach(host_if->arg, &as->codec_if))) {
900 free(as, M_DEVBUF);
901 return error;
902 }
903
904 host_if->reset(host_if->arg);
905
906 host_if->write(host_if->arg, AC97_REG_POWER, 0);
907 host_if->write(host_if->arg, AC97_REG_RESET, 0);
908
909 if (host_if->flags)
910 as->host_flags = host_if->flags(host_if->arg);
911
912 ac97_setup_defaults(as);
913 ac97_read(as, AC97_REG_RESET, &as->caps);
914 ac97_read(as, AC97_REG_VENDOR_ID1, &id1);
915 ac97_read(as, AC97_REG_VENDOR_ID2, &id2);
916
917 id = (id1 << 16) | id2;
918
919 aprint_normal("%s: ac97: ", sc_dev->dv_xname);
920
921 for (i = 0; ; i++) {
922 if (ac97codecid[i].id == 0) {
923 char pnp[4];
924
925 AC97_GET_CODEC_ID(id, pnp);
926 #define ISASCII(c) ((c) >= ' ' && (c) < 0x7f)
927 if (ISASCII(pnp[0]) && ISASCII(pnp[1]) &&
928 ISASCII(pnp[2]))
929 aprint_normal("%c%c%c%d",
930 pnp[0], pnp[1], pnp[2], pnp[3]);
931 else
932 aprint_normal("unknown (0x%08x)", id);
933 break;
934 }
935 if (ac97codecid[i].id == (id & ac97codecid[i].mask)) {
936 aprint_normal("%s", ac97codecid[i].name);
937 if (ac97codecid[i].mask == AC97_VENDOR_ID_MASK) {
938 aprint_normal(" (0x%08x)", id);
939 }
940 initfunc = ac97codecid[i].init;
941 break;
942 }
943 }
944 aprint_normal(" codec; ");
945 for (i = j = 0; i < 10; i++) {
946 if (as->caps & (1 << i)) {
947 aprint_normal("%s%s", j ? ", " : "", ac97feature[i]);
948 j++;
949 }
950 }
951 aprint_normal("%s%s\n", j ? ", " : "",
952 ac97enhancement[AC97_CAPS_ENHANCEMENT(as->caps)]);
953
954 as->ac97_clock = AC97_STANDARD_CLOCK;
955 ac97_read(as, AC97_REG_EXT_AUDIO_ID, &as->ext_id);
956 if (as->ext_id != 0) {
957 /* Print capabilities */
958 bitmask_snprintf(as->ext_id, "\20\20SECONDARY10\17SECONDARY01"
959 "\14AC97_23\13AC97_22\12AMAP\11LDAC\10SDAC"
960 "\7CDAC\4VRM\3SPDIF\2DRA\1VRA",
961 flagbuf, FLAGBUFLEN);
962 aprint_normal("%s: ac97: ext id %s\n", sc_dev->dv_xname, flagbuf);
963
964 /* Print unusual settings */
965 if (as->ext_id & AC97_EXT_AUDIO_DSA_MASK) {
966 aprint_normal("%s: ac97: Slot assignment: ",
967 sc_dev->dv_xname);
968 switch (as->ext_id & AC97_EXT_AUDIO_DSA_MASK) {
969 case AC97_EXT_AUDIO_DSA01:
970 aprint_normal("7&8, 6&9, 10&11.\n");
971 break;
972 case AC97_EXT_AUDIO_DSA10:
973 aprint_normal("6&9, 10&11, 3&4.\n");
974 break;
975 case AC97_EXT_AUDIO_DSA11:
976 aprint_normal("10&11, 3&4, 7&8.\n");
977 break;
978 }
979 }
980
981 /* Enable and disable features */
982 ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &extstat);
983 extstat &= ~AC97_EXT_AUDIO_DRA;
984 if (as->ext_id & AC97_EXT_AUDIO_LDAC)
985 extstat |= AC97_EXT_AUDIO_LDAC;
986 if (as->ext_id & AC97_EXT_AUDIO_SDAC)
987 extstat |= AC97_EXT_AUDIO_SDAC;
988 if (as->ext_id & AC97_EXT_AUDIO_CDAC)
989 extstat |= AC97_EXT_AUDIO_CDAC;
990 if (as->ext_id & AC97_EXT_AUDIO_VRM)
991 extstat |= AC97_EXT_AUDIO_VRM;
992 if (as->ext_id & AC97_EXT_AUDIO_SPDIF) {
993 /* Output the same data as DAC to SPDIF output */
994 extstat &= ~AC97_EXT_AUDIO_SPSA_MASK;
995 extstat |= AC97_EXT_AUDIO_SPSA34;
996 }
997 if (as->ext_id & AC97_EXT_AUDIO_VRA)
998 extstat |= AC97_EXT_AUDIO_VRA;
999 ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, extstat);
1000 if (as->ext_id & AC97_EXT_AUDIO_VRA) {
1001 /* VRA should be enabled. */
1002 /* so it claims to do variable rate, let's make sure */
1003 ac97_write(as, AC97_REG_PCM_FRONT_DAC_RATE, 44100);
1004 ac97_read(as, AC97_REG_PCM_FRONT_DAC_RATE, &rate);
1005 if (rate != 44100) {
1006 /* We can't believe ext_id */
1007 as->ext_id = 0;
1008 aprint_normal(
1009 "%s: Ignore these capabilities.\n",
1010 sc_dev->dv_xname);
1011 }
1012 /* restore the default value */
1013 ac97_write(as, AC97_REG_PCM_FRONT_DAC_RATE,
1014 AC97_SINGLE_RATE);
1015 }
1016 }
1017
1018 ac97_setup_source_info(as);
1019
1020 DELAY(900 * 1000);
1021 memset(&ctl, 0, sizeof(ctl));
1022
1023 /* disable mutes */
1024 for (i = 0; i < 11; i++) {
1025 static struct {
1026 char *class, *device;
1027 } d[11] = {
1028 { AudioCoutputs, AudioNmaster},
1029 { AudioCoutputs, AudioNheadphone},
1030 { AudioCoutputs, AudioNsurround},
1031 { AudioCoutputs, AudioNcenter},
1032 { AudioCoutputs, AudioNlfe},
1033 { AudioCinputs, AudioNdac},
1034 { AudioCinputs, AudioNcd},
1035 { AudioCinputs, AudioNline},
1036 { AudioCinputs, AudioNaux},
1037 { AudioCinputs, AudioNvideo},
1038 { AudioCrecord, AudioNvolume},
1039 };
1040
1041 ctl.type = AUDIO_MIXER_ENUM;
1042 ctl.un.ord = 0;
1043
1044 ctl.dev = ac97_get_portnum_by_name(&as->codec_if,
1045 d[i].class, d[i].device, AudioNmute);
1046 ac97_mixer_set_port(&as->codec_if, &ctl);
1047 }
1048 ctl.type = AUDIO_MIXER_ENUM;
1049 ctl.un.ord = 0;
1050 ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
1051 AudioNsource, NULL);
1052 ac97_mixer_set_port(&as->codec_if, &ctl);
1053
1054 /* set a reasonable default volume */
1055 ctl.type = AUDIO_MIXER_VALUE;
1056 ctl.un.value.num_channels = 2;
1057 ctl.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = \
1058 ctl.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 127;
1059 ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCoutputs,
1060 AudioNmaster, NULL);
1061 ac97_mixer_set_port(&as->codec_if, &ctl);
1062 ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCoutputs,
1063 AudioNsurround, NULL);
1064 ac97_mixer_set_port(&as->codec_if, &ctl);
1065 ctl.un.value.num_channels = 1;
1066 ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCoutputs,
1067 AudioNcenter, NULL);
1068 ac97_mixer_set_port(&as->codec_if, &ctl);
1069 ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCoutputs,
1070 AudioNlfe, NULL);
1071 ac97_mixer_set_port(&as->codec_if, &ctl);
1072
1073 if (initfunc != NULL)
1074 initfunc(as);
1075 return 0;
1076 }
1077
1078
1079 int
1080 ac97_query_devinfo(struct ac97_codec_if *codec_if, mixer_devinfo_t *dip)
1081 {
1082 struct ac97_softc *as;
1083 struct ac97_source_info *si;
1084 const char *name;
1085
1086 as = (struct ac97_softc *)codec_if;
1087 if (dip->index < as->num_source_info) {
1088 si = &as->source_info[dip->index];
1089 dip->type = si->type;
1090 dip->mixer_class = si->mixer_class;
1091 dip->prev = si->prev;
1092 dip->next = si->next;
1093
1094 if (si->qualifier)
1095 name = si->qualifier;
1096 else if (si->device)
1097 name = si->device;
1098 else if (si->class)
1099 name = si->class;
1100 else
1101 name = 0;
1102
1103 if (name)
1104 strcpy(dip->label.name, name);
1105
1106 memcpy(&dip->un, si->info, si->info_size);
1107
1108 /* Set the delta for volume sources */
1109 if (dip->type == AUDIO_MIXER_VALUE)
1110 dip->un.v.delta = 1 << (8 - si->bits);
1111
1112 return 0;
1113 }
1114
1115 return ENXIO;
1116 }
1117
1118
1119
1120 int
1121 ac97_mixer_set_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
1122 {
1123 struct ac97_softc *as;
1124 struct ac97_source_info *si;
1125 u_int16_t mask;
1126 u_int16_t val, newval;
1127 int error;
1128
1129 as = (struct ac97_softc *)codec_if;
1130 si = &as->source_info[cp->dev];
1131 if (cp->dev < 0 || cp->dev >= as->num_source_info)
1132 return EINVAL;
1133
1134 if (cp->type != si->type)
1135 return EINVAL;
1136
1137 ac97_read(as, si->reg, &val);
1138
1139 DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
1140
1141 mask = (1 << si->bits) - 1;
1142
1143 switch (cp->type) {
1144 case AUDIO_MIXER_ENUM:
1145 if (cp->un.ord > mask || cp->un.ord < 0)
1146 return EINVAL;
1147
1148 newval = (cp->un.ord << si->ofs);
1149 if (si->reg == AC97_REG_RECORD_SELECT) {
1150 newval |= (newval << (8 + si->ofs));
1151 mask |= (mask << 8);
1152 mask = mask << si->ofs;
1153 } else if (si->reg == AC97_REG_SURR_MASTER) {
1154 newval = cp->un.ord ? 0x8080 : 0x0000;
1155 mask = 0x8080;
1156 } else
1157 mask = mask << si->ofs;
1158 break;
1159 case AUDIO_MIXER_VALUE:
1160 {
1161 const struct audio_mixer_value *value = si->info;
1162 u_int16_t l, r, ol, or;
1163 int deltal, deltar;
1164
1165 if ((cp->un.value.num_channels <= 0) ||
1166 (cp->un.value.num_channels > value->num_channels))
1167 return EINVAL;
1168
1169 if (cp->un.value.num_channels == 1) {
1170 l = r = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
1171 } else {
1172 if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
1173 l = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
1174 r = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
1175 } else { /* left/right is reversed here */
1176 r = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
1177 l = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
1178 }
1179
1180 }
1181
1182 if (!si->polarity) {
1183 l = 255 - l;
1184 r = 255 - r;
1185 }
1186
1187 ol = (val >> (8+si->ofs)) & mask;
1188 or = (val >> si->ofs) & mask;
1189
1190 deltal = (ol << (8 - si->bits)) - l;
1191 deltar = (or << (8 - si->bits)) - r;
1192
1193 l = l >> (8 - si->bits);
1194 r = r >> (8 - si->bits);
1195
1196 if (deltal && ol == l)
1197 l += (deltal > 0) ? (l ? -1 : 0) : (l < mask ? 1 : 0);
1198 if (deltar && or == r)
1199 r += (deltar > 0) ? (r ? -1 : 0) : (r < mask ? 1 : 0);
1200
1201 newval = ((r & mask) << si->ofs);
1202 if (value->num_channels == 2) {
1203 newval = newval | ((l & mask) << (si->ofs+8));
1204 mask |= (mask << 8);
1205 }
1206 mask = mask << si->ofs;
1207 break;
1208 }
1209 default:
1210 return EINVAL;
1211 }
1212
1213 error = ac97_write(as, si->reg, (val & ~mask) | newval);
1214 if (error)
1215 return error;
1216
1217 return 0;
1218 }
1219
1220 int
1221 ac97_get_portnum_by_name(struct ac97_codec_if *codec_if, const char *class,
1222 const char *device, const char *qualifier)
1223 {
1224 struct ac97_softc *as;
1225 int idx;
1226
1227 as = (struct ac97_softc *)codec_if;
1228 for (idx = 0; idx < as->num_source_info; idx++) {
1229 struct ac97_source_info *si = &as->source_info[idx];
1230 if (ac97_str_equal(class, si->class) &&
1231 ac97_str_equal(device, si->device) &&
1232 ac97_str_equal(qualifier, si->qualifier))
1233 return idx;
1234 }
1235
1236 return -1;
1237 }
1238
1239 int
1240 ac97_mixer_get_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
1241 {
1242 struct ac97_softc *as;
1243 struct ac97_source_info *si;
1244 u_int16_t mask;
1245 u_int16_t val;
1246
1247 as = (struct ac97_softc *)codec_if;
1248 si = &as->source_info[cp->dev];
1249 if (cp->dev < 0 || cp->dev >= as->num_source_info)
1250 return EINVAL;
1251
1252 if (cp->type != si->type)
1253 return EINVAL;
1254
1255 ac97_read(as, si->reg, &val);
1256
1257 DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
1258
1259 mask = (1 << si->bits) - 1;
1260
1261 switch (cp->type) {
1262 case AUDIO_MIXER_ENUM:
1263 cp->un.ord = (val >> si->ofs) & mask;
1264 DPRINTFN(4, ("AUDIO_MIXER_ENUM: %x %d %x %d\n",
1265 val, si->ofs, mask, cp->un.ord));
1266 break;
1267 case AUDIO_MIXER_VALUE:
1268 {
1269 const struct audio_mixer_value *value = si->info;
1270 u_int16_t l, r;
1271
1272 if ((cp->un.value.num_channels <= 0) ||
1273 (cp->un.value.num_channels > value->num_channels))
1274 return EINVAL;
1275
1276 if (value->num_channels == 1) {
1277 l = r = (val >> si->ofs) & mask;
1278 } else {
1279 if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
1280 l = (val >> (si->ofs + 8)) & mask;
1281 r = (val >> si->ofs) & mask;
1282 } else { /* host has reversed channels */
1283 r = (val >> (si->ofs + 8)) & mask;
1284 l = (val >> si->ofs) & mask;
1285 }
1286 }
1287
1288 l = (l << (8 - si->bits));
1289 r = (r << (8 - si->bits));
1290 if (!si->polarity) {
1291 l = 255 - l;
1292 r = 255 - r;
1293 }
1294
1295 /* The EAP driver averages l and r for stereo
1296 channels that are requested in MONO mode. Does this
1297 make sense? */
1298 if (cp->un.value.num_channels == 1) {
1299 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = l;
1300 } else if (cp->un.value.num_channels == 2) {
1301 cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
1302 cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
1303 }
1304
1305 break;
1306 }
1307 default:
1308 return EINVAL;
1309 }
1310
1311 return 0;
1312 }
1313
1314
1315 int
1316 ac97_set_rate(struct ac97_codec_if *codec_if, int target, u_long *rate)
1317 {
1318 struct ac97_softc *as;
1319 u_long value;
1320 u_int16_t ext_stat;
1321 u_int16_t actual;
1322 u_int16_t power;
1323 u_int16_t power_bit;
1324
1325 as = (struct ac97_softc *)codec_if;
1326 if (target == AC97_REG_PCM_MIC_ADC_RATE) {
1327 if (!(as->ext_id & AC97_EXT_AUDIO_VRM)) {
1328 *rate = AC97_SINGLE_RATE;
1329 return 0;
1330 }
1331 } else {
1332 if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
1333 *rate = AC97_SINGLE_RATE;
1334 return 0;
1335 }
1336 }
1337 value = *rate * AC97_STANDARD_CLOCK / as->ac97_clock;
1338 ext_stat = 0;
1339 /*
1340 * PCM_FRONT_DAC_RATE/PCM_SURR_DAC_RATE/PCM_LFE_DAC_RATE
1341 * Check VRA, DRA
1342 * PCM_LR_ADC_RATE
1343 * Check VRA
1344 * PCM_MIC_ADC_RATE
1345 * Check VRM
1346 */
1347 switch (target) {
1348 case AC97_REG_PCM_FRONT_DAC_RATE:
1349 case AC97_REG_PCM_SURR_DAC_RATE:
1350 case AC97_REG_PCM_LFE_DAC_RATE:
1351 power_bit = AC97_POWER_OUT;
1352 if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
1353 *rate = AC97_SINGLE_RATE;
1354 return 0;
1355 }
1356 if (as->ext_id & AC97_EXT_AUDIO_DRA) {
1357 ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &ext_stat);
1358 if (value > 0x1ffff) {
1359 return EINVAL;
1360 } else if (value > 0xffff) {
1361 /* Enable DRA */
1362 ext_stat |= AC97_EXT_AUDIO_DRA;
1363 ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, ext_stat);
1364 value /= 2;
1365 } else {
1366 /* Disable DRA */
1367 ext_stat &= ~AC97_EXT_AUDIO_DRA;
1368 ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, ext_stat);
1369 }
1370 } else {
1371 if (value > 0xffff)
1372 return EINVAL;
1373 }
1374 break;
1375 case AC97_REG_PCM_LR_ADC_RATE:
1376 power_bit = AC97_POWER_IN;
1377 if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
1378 *rate = AC97_SINGLE_RATE;
1379 return 0;
1380 }
1381 if (value > 0xffff)
1382 return EINVAL;
1383 break;
1384 case AC97_REG_PCM_MIC_ADC_RATE:
1385 power_bit = AC97_POWER_IN;
1386 if (!(as->ext_id & AC97_EXT_AUDIO_VRM)) {
1387 *rate = AC97_SINGLE_RATE;
1388 return 0;
1389 }
1390 if (value > 0xffff)
1391 return EINVAL;
1392 break;
1393 default:
1394 printf("%s: Unknown register: 0x%x\n", __func__, target);
1395 return EINVAL;
1396 }
1397
1398 ac97_read(as, AC97_REG_POWER, &power);
1399 ac97_write(as, AC97_REG_POWER, power | power_bit);
1400
1401 ac97_write(as, target, (u_int16_t)value);
1402 ac97_read(as, target, &actual);
1403 actual = (u_int32_t)actual * as->ac97_clock / AC97_STANDARD_CLOCK;
1404
1405 ac97_write(as, AC97_REG_POWER, power);
1406 if (ext_stat & AC97_EXT_AUDIO_DRA) {
1407 *rate = actual * 2;
1408 } else {
1409 *rate = actual;
1410 }
1411 return 0;
1412 }
1413
1414 void
1415 ac97_set_clock(struct ac97_codec_if *codec_if, unsigned int clock)
1416 {
1417 struct ac97_softc *as;
1418
1419 as = (struct ac97_softc *)codec_if;
1420 as->ac97_clock = clock;
1421 }
1422
1423 u_int16_t
1424 ac97_get_extcaps(struct ac97_codec_if *codec_if)
1425 {
1426 struct ac97_softc *as;
1427
1428 as = (struct ac97_softc *)codec_if;
1429 return as->ext_id;
1430 }
1431
1432 int
1433 ac97_add_port(struct ac97_softc *as, const struct ac97_source_info *src)
1434 {
1435 struct ac97_source_info *si;
1436 int ouridx, idx;
1437
1438 if (as->num_source_info >= MAX_SOURCES) {
1439 printf("%s: internal error: increase MAX_SOURCES in %s\n",
1440 __func__, __FILE__);
1441 return -1;
1442 }
1443 if (!ac97_check_capability(as, src->req_feature))
1444 return -1;
1445 ouridx = as->num_source_info;
1446 si = &as->source_info[ouridx];
1447 memcpy(si, src, sizeof(*si));
1448
1449 switch (si->type) {
1450 case AUDIO_MIXER_CLASS:
1451 case AUDIO_MIXER_VALUE:
1452 printf("%s: adding class/value is not supported yet.\n",
1453 __func__);
1454 return -1;
1455 case AUDIO_MIXER_ENUM:
1456 break;
1457 default:
1458 printf("%s: unknown type: %d\n", __func__, si->type);
1459 return -1;
1460 }
1461 as->num_source_info++;
1462
1463 si->mixer_class = ac97_get_portnum_by_name(&as->codec_if, si->class,
1464 NULL, NULL);
1465 /* Find the root of the device */
1466 idx = ac97_get_portnum_by_name(&as->codec_if, si->class,
1467 si->device, NULL);
1468 /* Find the last item */
1469 while (as->source_info[idx].next != AUDIO_MIXER_LAST)
1470 idx = as->source_info[idx].next;
1471 /* Append */
1472 as->source_info[idx].next = ouridx;
1473 si->prev = idx;
1474 si->next = AUDIO_MIXER_LAST;
1475
1476 return 0;
1477 }
1478
1479 /**
1480 * Codec-dependent initialization
1481 */
1482
1483 #define AD1980_REG_MISC 0x76
1484 #define AD1980_MISC_MBG0 0x0001 /* 0 1888/1980/1981 /1985 */
1485 #define AD1980_MISC_MBG1 0x0002 /* 1 1888/1980/1981 /1985 */
1486 #define AD1980_MISC_VREFD 0x0004 /* 2 1888/1980/1981 /1985 */
1487 #define AD1980_MISC_VREFH 0x0008 /* 3 1888/1980/1981 /1985 */
1488 #define AD1980_MISC_SRU 0x0010 /* 4 1888/1980 /1985 */
1489 #define AD1980_MISC_LOSEL 0x0020 /* 5 1888/1980/1981 /1985 */
1490 #define AD1980_MISC_2CMIC 0x0040 /* 6 1980/1981B/1985 */
1491 #define AD1980_MISC_SPRD 0x0080 /* 7 1888/1980 /1985 */
1492 #define AD1980_MISC_DMIX0 0x0100 /* 8 1888/1980 /1985 */
1493 #define AD1980_MISC_DMIX1 0x0200 /* 9 1888/1980 /1985 */
1494 #define AD1980_MISC_HPSEL 0x0400 /*10 1888/1980 /1985 */
1495 #define AD1980_MISC_CLDIS 0x0800 /*11 1888/1980 /1985 */
1496 #define AD1980_MISC_LODIS 0x1000 /*12 1888/1980/1981 /1985 */
1497 #define AD1980_MISC_MSPLT 0x2000 /*13 1888/1980/1981 /1985 */
1498 #define AD1980_MISC_AC97NC 0x4000 /*14 1888/1980 /1985 */
1499 #define AD1980_MISC_DACZ 0x8000 /*15 1888/1980/1981 /1985 */
1500 #define AD1981_REG_MISC 0x76
1501 #define AD1981_MISC_MADST 0x0010 /* 4 */
1502 #define AD1981A_MISC_MADPD 0x0040 /* 6 */
1503 #define AD1981B_MISC_MADPD 0x0080 /* 7 */
1504 #define AD1981_MISC_FMXE 0x0200 /* 9 */
1505 #define AD1981_MISC_DAM 0x0800 /*11 */
1506 static void
1507 ac97_ad198x_init(struct ac97_softc *as)
1508 {
1509 int i;
1510 uint16_t misc;
1511
1512 ac97_read(as, AD1980_REG_MISC, &misc);
1513 ac97_write(as, AD1980_REG_MISC,
1514 misc | AD1980_MISC_LOSEL | AD1980_MISC_HPSEL);
1515
1516 for (i = 0; i < as->num_source_info; i++) {
1517 if (as->source_info[i].type != AUDIO_MIXER_VALUE)
1518 continue;
1519
1520 if (as->source_info[i].reg == AC97_REG_MASTER_VOLUME)
1521 as->source_info[i].reg = AC97_REG_SURR_MASTER;
1522 else if (as->source_info[i].reg == AC97_REG_SURR_MASTER)
1523 as->source_info[i].reg = AC97_REG_MASTER_VOLUME;
1524 }
1525 }
1526
1527 #define ALC650_REG_MULTI_CHANNEL_CONTROL 0x6a
1528 #define ALC650_MCC_SLOT_MODIFY_MASK 0xc000
1529 #define ALC650_MCC_FRONTDAC_FROM_SPDIFIN 0x2000 /* 13 */
1530 #define ALC650_MCC_SPDIFOUT_FROM_ADC 0x1000 /* 12 */
1531 #define ALC650_MCC_PCM_FROM_SPDIFIN 0x0800 /* 11 */
1532 #define ALC650_MCC_MIC_OR_CENTERLFE 0x0400 /* 10 */
1533 #define ALC650_MCC_LINEIN_OR_SURROUND 0x0200 /* 9 */
1534 #define ALC650_MCC_INDEPENDENT_MASTER_L 0x0080 /* 7 */
1535 #define ALC650_MCC_INDEPENDENT_MASTER_R 0x0040 /* 6 */
1536 #define ALC650_MCC_ANALOG_TO_CENTERLFE 0x0020 /* 5 */
1537 #define ALC650_MCC_ANALOG_TO_SURROUND 0x0010 /* 4 */
1538 #define ALC650_MCC_EXCHANGE_CENTERLFE 0x0008 /* 3 */
1539 #define ALC650_MCC_CENTERLFE_DOWNMIX 0x0004 /* 2 */
1540 #define ALC650_MCC_SURROUND_DOWNMIX 0x0002 /* 1 */
1541 #define ALC650_MCC_LINEOUT_TO_SURROUND 0x0001 /* 0 */
1542 static void
1543 ac97_alc650_init(struct ac97_softc *as)
1544 {
1545 static const struct ac97_source_info sources[6] = {
1546 { AudioCoutputs, AudioNsurround, "lineinjack",
1547 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1548 ALC650_REG_MULTI_CHANNEL_CONTROL,
1549 0x0000, 1, 9, 0, 0, CHECK_SURROUND },
1550 { AudioCoutputs, AudioNsurround, "mixtofront",
1551 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1552 ALC650_REG_MULTI_CHANNEL_CONTROL,
1553 0x0000, 1, 1, 0, 0, CHECK_SURROUND },
1554 { AudioCoutputs, AudioNcenter, "micjack",
1555 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1556 ALC650_REG_MULTI_CHANNEL_CONTROL,
1557 0x0000, 1, 10, 0, 0, CHECK_CENTER },
1558 { AudioCoutputs, AudioNlfe, "micjack",
1559 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1560 ALC650_REG_MULTI_CHANNEL_CONTROL,
1561 0x0000, 1, 10, 0, 0, CHECK_LFE },
1562 { AudioCoutputs, AudioNcenter, "mixtofront",
1563 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1564 ALC650_REG_MULTI_CHANNEL_CONTROL,
1565 0x0000, 1, 2, 0, 0, CHECK_CENTER },
1566 { AudioCoutputs, AudioNlfe, "mixtofront",
1567 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1568 ALC650_REG_MULTI_CHANNEL_CONTROL,
1569 0x0000, 1, 2, 0, 0, CHECK_LFE },
1570 };
1571
1572 ac97_add_port(as, &sources[0]);
1573 ac97_add_port(as, &sources[1]);
1574 ac97_add_port(as, &sources[2]);
1575 ac97_add_port(as, &sources[3]);
1576 ac97_add_port(as, &sources[4]);
1577 ac97_add_port(as, &sources[5]);
1578 }
1579
1580 #define VT1616_REG_IO_CONTROL 0x5a
1581 #define VT1616_IC_LVL (1 << 15)
1582 #define VT1616_IC_LFECENTER_TO_FRONT (1 << 12)
1583 #define VT1616_IC_SURROUND_TO_FRONT (1 << 11)
1584 #define VT1616_IC_BPDC (1 << 10)
1585 #define VT1616_IC_DC (1 << 9)
1586 #define VT1616_IC_IB_MASK 0x000c
1587 static void
1588 ac97_vt1616_init(struct ac97_softc *as)
1589 {
1590 static const struct ac97_source_info sources[3] = {
1591 { AudioCoutputs, AudioNsurround, "mixtofront",
1592 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1593 VT1616_REG_IO_CONTROL,
1594 0x0000, 1, 11, 0, 0, CHECK_SURROUND },
1595 { AudioCoutputs, AudioNcenter, "mixtofront",
1596 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1597 VT1616_REG_IO_CONTROL,
1598 0x0000, 1, 12, 0, 0, CHECK_CENTER },
1599 { AudioCoutputs, AudioNlfe, "mixtofront",
1600 AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
1601 VT1616_REG_IO_CONTROL,
1602 0x0000, 1, 12, 0, 0, CHECK_LFE },
1603 };
1604
1605 ac97_add_port(as, &sources[0]);
1606 ac97_add_port(as, &sources[1]);
1607 ac97_add_port(as, &sources[2]);
1608 }
1609