1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #ifdef HAVE_KERNEL_OPTION_HEADERS
30 #include "opt_snd.h"
31 #endif
32
33 #include <dev/sound/pcm/sound.h>
34 #include <dev/sound/pcm/ac97.h>
35
36 #include <dev/pci/pcivar.h>
37
38 #include "mixer_if.h"
39
40 static MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
41
42 typedef void (*ac97_patch)(struct ac97_info *);
43
44 struct ac97mixtable_entry {
45 int reg; /* register index */
46 /* reg < 0 if inverted polarity */
47 unsigned bits:4; /* width of control field */
48 unsigned ofs:4; /* offset (only if stereo=0) */
49 unsigned stereo:1; /* set for stereo controls */
50 unsigned mute:1; /* bit15 is MUTE */
51 unsigned recidx:4; /* index in rec mux */
52 unsigned mask:1; /* use only masked bits */
53 unsigned enable:1; /* entry is enabled */
54 };
55
56 #define AC97_MIXER_SIZE SOUND_MIXER_NRDEVICES
57
58 struct ac97_info {
59 kobj_t methods;
60 device_t dev;
61 void *devinfo;
62 u_int32_t id;
63 u_int32_t subvendor;
64 unsigned count, caps, se, extcaps, extid, extstat, noext:1;
65 u_int32_t flags;
66 struct ac97mixtable_entry mix[AC97_MIXER_SIZE];
67 char name[16];
68 struct mtx lock;
69 };
70
71 struct ac97_vendorid {
72 u_int32_t id;
73 const char *name;
74 };
75
76 struct ac97_codecid {
77 u_int32_t id;
78 u_int8_t stepmask;
79 u_int8_t noext:1;
80 char *name;
81 ac97_patch patch;
82 };
83
84 static const struct ac97mixtable_entry ac97mixtable_default[AC97_MIXER_SIZE] = {
85 /* [offset] reg bits of st mu re mk en */
86 [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 },
87 [SOUND_MIXER_OGAIN] = { AC97_MIX_AUXOUT, 5, 0, 1, 1, 0, 0, 0 },
88 [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 },
89 [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 },
90 [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 },
91 [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 },
92 [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 },
93 [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 },
94 [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 },
95 [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 1, 1 },
96 /* use igain for the mic 20dB boost */
97 [SOUND_MIXER_IGAIN] = { -AC97_MIX_MIC, 1, 6, 0, 0, 0, 1, 1 },
98 [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 },
99 [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 },
100 [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 },
101 [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 }
102 };
103
104 static const struct ac97_vendorid ac97vendorid[] = {
105 { 0x41445300, "Analog Devices" },
106 { 0x414b4d00, "Asahi Kasei" },
107 { 0x414c4300, "Realtek" },
108 { 0x414c4700, "Avance Logic" },
109 { 0x43525900, "Cirrus Logic" },
110 { 0x434d4900, "C-Media Electronics" },
111 { 0x43585400, "Conexant" },
112 { 0x44543000, "Diamond Technology" },
113 { 0x454d4300, "eMicro" },
114 { 0x45838300, "ESS Technology" },
115 { 0x48525300, "Intersil" },
116 { 0x49434500, "ICEnsemble" },
117 { 0x49544500, "ITE, Inc." },
118 { 0x4e534300, "National Semiconductor" },
119 { 0x50534300, "Philips Semiconductor" },
120 { 0x83847600, "SigmaTel" },
121 { 0x53494c00, "Silicon Laboratories" },
122 { 0x54524100, "TriTech" },
123 { 0x54584e00, "Texas Instruments" },
124 { 0x56494100, "VIA Technologies" },
125 { 0x57454300, "Winbond" },
126 { 0x574d4c00, "Wolfson" },
127 { 0x594d4800, "Yamaha" },
128 { 0x01408300, "SigmaTel" },
129 { 0x00000000, NULL }
130 };
131
132 static void ad1886_patch(struct ac97_info *);
133 static void ad198x_patch(struct ac97_info *);
134 static void ad1981b_patch(struct ac97_info *);
135 static void cmi9739_patch(struct ac97_info *);
136 static void alc655_patch(struct ac97_info *);
137
138 static struct ac97_codecid ac97codecid[] = {
139 { 0x41445303, 0x00, 0, "AD1819", 0 },
140 { 0x41445340, 0x00, 0, "AD1881", 0 },
141 { 0x41445348, 0x00, 0, "AD1881A", 0 },
142 { 0x41445360, 0x00, 0, "AD1885", 0 },
143 { 0x41445361, 0x00, 0, "AD1886", ad1886_patch },
144 { 0x41445362, 0x00, 0, "AD1887", 0 },
145 { 0x41445363, 0x00, 0, "AD1886A", 0 },
146 { 0x41445368, 0x00, 0, "AD1888", ad198x_patch },
147 { 0x41445370, 0x00, 0, "AD1980", ad198x_patch },
148 { 0x41445372, 0x00, 0, "AD1981A", 0 },
149 { 0x41445374, 0x00, 0, "AD1981B", ad1981b_patch },
150 { 0x41445375, 0x00, 0, "AD1985", ad198x_patch },
151 { 0x41445378, 0x00, 0, "AD1986", ad198x_patch },
152 { 0x414b4d00, 0x00, 1, "AK4540", 0 },
153 { 0x414b4d01, 0x00, 1, "AK4542", 0 },
154 { 0x414b4d02, 0x00, 1, "AK4543", 0 },
155 { 0x414b4d06, 0x00, 0, "AK4544A", 0 },
156 { 0x454b4d07, 0x00, 0, "AK4545", 0 },
157 { 0x414c4320, 0x0f, 0, "ALC100", 0 },
158 { 0x414c4730, 0x0f, 0, "ALC101", 0 },
159 { 0x414c4710, 0x0f, 0, "ALC200", 0 },
160 { 0x414c4740, 0x0f, 0, "ALC202", 0 },
161 { 0x414c4720, 0x0f, 0, "ALC650", 0 },
162 { 0x414c4752, 0x0f, 0, "ALC250", 0 },
163 { 0x414c4760, 0x0f, 0, "ALC655", alc655_patch },
164 { 0x414c4770, 0x0f, 0, "ALC203", 0 },
165 { 0x414c4780, 0x0f, 0, "ALC658", 0 },
166 { 0x414c4790, 0x0f, 0, "ALC850", 0 },
167 { 0x43525900, 0x07, 0, "CS4297", 0 },
168 { 0x43525910, 0x07, 0, "CS4297A", 0 },
169 { 0x43525920, 0x07, 0, "CS4294/98", 0 },
170 { 0x4352592d, 0x07, 0, "CS4294", 0 },
171 { 0x43525930, 0x07, 0, "CS4299", 0 },
172 { 0x43525940, 0x07, 0, "CS4201", 0 },
173 { 0x43525958, 0x07, 0, "CS4205", 0 },
174 { 0x43525960, 0x07, 0, "CS4291A", 0 },
175 { 0x434d4961, 0x00, 0, "CMI9739", cmi9739_patch },
176 { 0x434d4941, 0x00, 0, "CMI9738", 0 },
177 { 0x434d4978, 0x00, 0, "CMI9761", 0 },
178 { 0x434d4982, 0x00, 0, "CMI9761", 0 },
179 { 0x434d4983, 0x00, 0, "CMI9761", 0 },
180 { 0x43585421, 0x00, 0, "HSD11246", 0 },
181 { 0x43585428, 0x07, 0, "CX20468", 0 },
182 { 0x43585430, 0x00, 0, "CX20468-21", 0 },
183 { 0x44543000, 0x00, 0, "DT0398", 0 },
184 { 0x454d4323, 0x00, 0, "EM28023", 0 },
185 { 0x454d4328, 0x00, 0, "EM28028", 0 },
186 { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */
187 { 0x48525300, 0x00, 0, "HMP9701", 0 },
188 { 0x49434501, 0x00, 0, "ICE1230", 0 },
189 { 0x49434511, 0x00, 0, "ICE1232", 0 },
190 { 0x49434514, 0x00, 0, "ICE1232A", 0 },
191 { 0x49434551, 0x03, 0, "VT1616", 0 }, /* Via badged ICE */
192 { 0x49544520, 0x00, 0, "ITE2226E", 0 },
193 { 0x49544560, 0x07, 0, "ITE2646E", 0 }, /* XXX: patch needed */
194 { 0x4e534340, 0x00, 0, "LM4540", 0 }, /* Spec blank on revid */
195 { 0x4e534343, 0x00, 0, "LM4543", 0 }, /* Ditto */
196 { 0x4e534346, 0x00, 0, "LM4546A", 0 },
197 { 0x4e534348, 0x00, 0, "LM4548A", 0 },
198 { 0x4e534331, 0x00, 0, "LM4549", 0 },
199 { 0x4e534349, 0x00, 0, "LM4549A", 0 },
200 { 0x4e534350, 0x00, 0, "LM4550", 0 },
201 { 0x50534301, 0x00, 0, "UCB1510", 0 },
202 { 0x50534304, 0x00, 0, "UCB1400", 0 },
203 { 0x83847600, 0x00, 0, "STAC9700/83/84", 0 },
204 { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
205 { 0x83847605, 0x00, 0, "STAC9704", 0 },
206 { 0x83847608, 0x00, 0, "STAC9708/11", 0 },
207 { 0x83847609, 0x00, 0, "STAC9721/23", 0 },
208 { 0x83847644, 0x00, 0, "STAC9744/45", 0 },
209 { 0x83847650, 0x00, 0, "STAC9750/51", 0 },
210 { 0x83847652, 0x00, 0, "STAC9752/53", 0 },
211 { 0x83847656, 0x00, 0, "STAC9756/57", 0 },
212 { 0x83847658, 0x00, 0, "STAC9758/59", 0 },
213 { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */
214 { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */
215 { 0x83847666, 0x00, 0, "STAC9766/67", 0 },
216 { 0x53494c22, 0x00, 0, "Si3036", 0 },
217 { 0x53494c23, 0x00, 0, "Si3038", 0 },
218 { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */
219 { 0x54524106, 0x00, 0, "TR28026", 0 },
220 { 0x54524108, 0x00, 0, "TR28028", 0 },
221 { 0x54524123, 0x00, 0, "TR28602", 0 },
222 { 0x54524e03, 0x07, 0, "TLV320AIC27", 0 },
223 { 0x54584e20, 0x00, 0, "TLC320AD90", 0 },
224 { 0x56494161, 0x00, 0, "VIA1612A", 0 },
225 { 0x56494170, 0x00, 0, "VIA1617A", 0 },
226 { 0x574d4c00, 0x00, 0, "WM9701A", 0 },
227 { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 },
228 { 0x574d4c04, 0x00, 0, "WM9704Q", 0 },
229 { 0x574d4c05, 0x00, 0, "WM9705/10", 0 },
230 { 0x574d4d09, 0x00, 0, "WM9709", 0 },
231 { 0x574d4c12, 0x00, 0, "WM9711/12", 0 }, /* XXX: patch needed */
232 { 0x57454301, 0x00, 0, "W83971D", 0 },
233 { 0x594d4800, 0x00, 0, "YMF743", 0 },
234 { 0x594d4802, 0x00, 0, "YMF752", 0 },
235 { 0x594d4803, 0x00, 0, "YMF753", 0 },
236 { 0x01408384, 0x00, 0, "STAC9704", 0 },
237 { 0, 0, 0, NULL, 0 }
238 };
239
240 static char *ac97enhancement[] = {
241 "no 3D Stereo Enhancement",
242 "Analog Devices Phat Stereo",
243 "Creative Stereo Enhancement",
244 "National Semi 3D Stereo Enhancement",
245 "Yamaha Ymersion",
246 "BBE 3D Stereo Enhancement",
247 "Crystal Semi 3D Stereo Enhancement",
248 "Qsound QXpander",
249 "Spatializer 3D Stereo Enhancement",
250 "SRS 3D Stereo Enhancement",
251 "Platform Tech 3D Stereo Enhancement",
252 "AKM 3D Audio",
253 "Aureal Stereo Enhancement",
254 "Aztech 3D Enhancement",
255 "Binaura 3D Audio Enhancement",
256 "ESS Technology Stereo Enhancement",
257 "Harman International VMAx",
258 "Nvidea 3D Stereo Enhancement",
259 "Philips Incredible Sound",
260 "Texas Instruments 3D Stereo Enhancement",
261 "VLSI Technology 3D Stereo Enhancement",
262 "TriTech 3D Stereo Enhancement",
263 "Realtek 3D Stereo Enhancement",
264 "Samsung 3D Stereo Enhancement",
265 "Wolfson Microelectronics 3D Enhancement",
266 "Delta Integration 3D Enhancement",
267 "SigmaTel 3D Enhancement",
268 "Reserved 27",
269 "Rockwell 3D Stereo Enhancement",
270 "Reserved 29",
271 "Reserved 30",
272 "Reserved 31"
273 };
274
275 static char *ac97feature[] = {
276 "mic channel",
277 "reserved",
278 "tone",
279 "simulated stereo",
280 "headphone",
281 "bass boost",
282 "18 bit DAC",
283 "20 bit DAC",
284 "18 bit ADC",
285 "20 bit ADC"
286 };
287
288 static char *ac97extfeature[] = {
289 "variable rate PCM",
290 "double rate PCM",
291 "reserved 1",
292 "variable rate mic",
293 "reserved 2",
294 "reserved 3",
295 "center DAC",
296 "surround DAC",
297 "LFE DAC",
298 "AMAP",
299 "reserved 4",
300 "reserved 5",
301 "reserved 6",
302 "reserved 7",
303 };
304
305 u_int16_t
ac97_rdcd(struct ac97_info * codec,int reg)306 ac97_rdcd(struct ac97_info *codec, int reg)
307 {
308 if (codec->flags & AC97_F_RDCD_BUG) {
309 u_int16_t i[2], j = 100;
310
311 i[0] = AC97_READ(codec->methods, codec->devinfo, reg);
312 i[1] = AC97_READ(codec->methods, codec->devinfo, reg);
313 while (i[0] != i[1] && j)
314 i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg);
315 return i[!(j & 1)];
316 }
317 return AC97_READ(codec->methods, codec->devinfo, reg);
318 }
319
320 void
ac97_wrcd(struct ac97_info * codec,int reg,u_int16_t val)321 ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
322 {
323 AC97_WRITE(codec->methods, codec->devinfo, reg, val);
324 }
325
326 static void
ac97_reset(struct ac97_info * codec)327 ac97_reset(struct ac97_info *codec)
328 {
329 u_int32_t i, ps;
330 ac97_wrcd(codec, AC97_REG_RESET, 0);
331 for (i = 0; i < 500; i++) {
332 ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
333 if (ps == AC97_POWER_STATUS)
334 return;
335 DELAY(1000);
336 }
337 device_printf(codec->dev, "AC97 reset timed out.\n");
338 }
339
340 int
ac97_setrate(struct ac97_info * codec,int which,int rate)341 ac97_setrate(struct ac97_info *codec, int which, int rate)
342 {
343 u_int16_t v;
344
345 switch(which) {
346 case AC97_REGEXT_FDACRATE:
347 case AC97_REGEXT_SDACRATE:
348 case AC97_REGEXT_LDACRATE:
349 case AC97_REGEXT_LADCRATE:
350 case AC97_REGEXT_MADCRATE:
351 break;
352
353 default:
354 return -1;
355 }
356
357 mtx_lock(&codec->lock);
358 if (rate != 0) {
359 v = rate;
360 if (codec->extstat & AC97_EXTCAP_DRA)
361 v >>= 1;
362 ac97_wrcd(codec, which, v);
363 }
364 v = ac97_rdcd(codec, which);
365 if (codec->extstat & AC97_EXTCAP_DRA)
366 v <<= 1;
367 mtx_unlock(&codec->lock);
368 return v;
369 }
370
371 int
ac97_setextmode(struct ac97_info * codec,u_int16_t mode)372 ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
373 {
374 mode &= AC97_EXTCAPS;
375 if ((mode & ~codec->extcaps) != 0) {
376 device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
377 mode);
378 return -1;
379 }
380 mtx_lock(&codec->lock);
381 ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
382 codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
383 mtx_unlock(&codec->lock);
384 return (mode == codec->extstat)? 0 : -1;
385 }
386
387 u_int16_t
ac97_getextmode(struct ac97_info * codec)388 ac97_getextmode(struct ac97_info *codec)
389 {
390 return codec->extstat;
391 }
392
393 u_int16_t
ac97_getextcaps(struct ac97_info * codec)394 ac97_getextcaps(struct ac97_info *codec)
395 {
396 return codec->extcaps;
397 }
398
399 u_int16_t
ac97_getcaps(struct ac97_info * codec)400 ac97_getcaps(struct ac97_info *codec)
401 {
402 return codec->caps;
403 }
404
405 u_int32_t
ac97_getsubvendor(struct ac97_info * codec)406 ac97_getsubvendor(struct ac97_info *codec)
407 {
408 return codec->subvendor;
409 }
410
411 static int
ac97_setrecsrc(struct ac97_info * codec,int channel)412 ac97_setrecsrc(struct ac97_info *codec, int channel)
413 {
414 struct ac97mixtable_entry *e = &codec->mix[channel];
415
416 if (e->recidx > 0) {
417 int val = e->recidx - 1;
418 val |= val << 8;
419 mtx_lock(&codec->lock);
420 ac97_wrcd(codec, AC97_REG_RECSEL, val);
421 mtx_unlock(&codec->lock);
422 return 0;
423 } else
424 return -1;
425 }
426
427 static int
ac97_setmixer(struct ac97_info * codec,unsigned channel,unsigned left,unsigned right)428 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
429 {
430 struct ac97mixtable_entry *e = &codec->mix[channel];
431
432 if (e->reg && e->enable && e->bits) {
433 int mask, max, val, reg;
434
435 reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register */
436 max = (1 << e->bits) - 1; /* actual range */
437 mask = (max << 8) | max; /* bits of interest */
438
439 if (!e->stereo)
440 right = left;
441
442 /*
443 * Invert the range if the polarity requires so,
444 * then scale to 0..max-1 to compute the value to
445 * write into the codec, and scale back to 0..100
446 * for the return value.
447 */
448 if (e->reg > 0) {
449 left = 100 - left;
450 right = 100 - right;
451 }
452
453 left = (left * max) / 100;
454 right = (right * max) / 100;
455
456 val = (left << 8) | right;
457
458 left = (left * 100) / max;
459 right = (right * 100) / max;
460
461 if (e->reg > 0) {
462 left = 100 - left;
463 right = 100 - right;
464 }
465
466 /*
467 * For mono controls, trim val and mask, also taking
468 * care of e->ofs (offset of control field).
469 */
470 if (e->ofs) {
471 val &= max;
472 val <<= e->ofs;
473 mask = (max << e->ofs);
474 }
475
476 /*
477 * If we have a mute bit, add it to the mask and
478 * update val and set mute if both channels require a
479 * zero volume.
480 */
481 if (e->mute == 1) {
482 mask |= AC97_MUTE;
483 if (left == 0 && right == 0)
484 val = AC97_MUTE;
485 }
486
487 /*
488 * If the mask bit is set, do not alter the other bits.
489 */
490 mtx_lock(&codec->lock);
491 if (e->mask) {
492 int cur = ac97_rdcd(codec, reg);
493 val |= cur & ~(mask);
494 }
495 ac97_wrcd(codec, reg, val);
496 mtx_unlock(&codec->lock);
497 return left | (right << 8);
498 } else {
499 return -1;
500 }
501 }
502
503 static void
ac97_fix_auxout(struct ac97_info * codec)504 ac97_fix_auxout(struct ac97_info *codec)
505 {
506 int keep_ogain;
507
508 /*
509 * By default, The ac97 aux_out register (0x04) corresponds to OSS's
510 * OGAIN setting.
511 *
512 * We first check whether aux_out is a valid register. If not
513 * we may not want to keep ogain.
514 */
515 keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000;
516
517 /*
518 * Determine what AUX_OUT really means, it can be:
519 *
520 * 1. Headphone out.
521 * 2. 4-Channel Out
522 * 3. True line level out (effectively master volume).
523 *
524 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
525 */
526 if (codec->extcaps & AC97_EXTCAP_SDAC &&
527 ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
528 codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND;
529 keep_ogain = 1;
530 }
531
532 if (keep_ogain == 0) {
533 bzero(&codec->mix[SOUND_MIXER_OGAIN],
534 sizeof(codec->mix[SOUND_MIXER_OGAIN]));
535 }
536 }
537
538 static void
ac97_fix_tone(struct ac97_info * codec)539 ac97_fix_tone(struct ac97_info *codec)
540 {
541 /*
542 * YMF chips does not indicate tone and 3D enhancement capability
543 * in the AC97_REG_RESET register.
544 */
545 switch (codec->id) {
546 case 0x594d4800: /* YMF743 */
547 case 0x594d4803: /* YMF753 */
548 codec->caps |= AC97_CAP_TONE;
549 codec->se |= 0x04;
550 break;
551 case 0x594d4802: /* YMF752 */
552 codec->se |= 0x04;
553 break;
554 default:
555 break;
556 }
557
558 /* Hide treble and bass if they don't exist */
559 if ((codec->caps & AC97_CAP_TONE) == 0) {
560 bzero(&codec->mix[SOUND_MIXER_BASS],
561 sizeof(codec->mix[SOUND_MIXER_BASS]));
562 bzero(&codec->mix[SOUND_MIXER_TREBLE],
563 sizeof(codec->mix[SOUND_MIXER_TREBLE]));
564 }
565 }
566
567 static const char*
ac97_hw_desc(u_int32_t id,const char * vname,const char * cname,char * buf)568 ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
569 {
570 if (cname == NULL) {
571 sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
572 return buf;
573 }
574
575 if (vname == NULL) vname = "Unknown";
576
577 if (bootverbose) {
578 sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
579 } else {
580 sprintf(buf, "%s %s AC97 Codec", vname, cname);
581 }
582 return buf;
583 }
584
585 static unsigned
ac97_initmixer(struct ac97_info * codec)586 ac97_initmixer(struct ac97_info *codec)
587 {
588 ac97_patch codec_patch;
589 const char *cname, *vname;
590 char desc[80];
591 device_t pdev;
592 unsigned i, j, k, bit, old;
593 u_int32_t id;
594 int reg;
595
596 mtx_lock(&codec->lock);
597 codec->count = AC97_INIT(codec->methods, codec->devinfo);
598 if (codec->count == 0) {
599 device_printf(codec->dev, "ac97 codec init failed\n");
600 mtx_unlock(&codec->lock);
601 return ENODEV;
602 }
603
604 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
605 ac97_reset(codec);
606 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
607
608 i = ac97_rdcd(codec, AC97_REG_RESET);
609 j = ac97_rdcd(codec, AC97_REG_RESET);
610 k = ac97_rdcd(codec, AC97_REG_RESET);
611 /*
612 * Let see if this codec can return consistent value.
613 * If not, turn on aggressive read workaround
614 * (STAC9704 comes in mind).
615 */
616 if (i != j || j != k) {
617 codec->flags |= AC97_F_RDCD_BUG;
618 i = ac97_rdcd(codec, AC97_REG_RESET);
619 }
620 codec->caps = i & 0x03ff;
621 codec->se = (i & 0x7c00) >> 10;
622
623 id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
624 if (id == 0 || id == 0xffffffff) {
625 device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
626 mtx_unlock(&codec->lock);
627 return ENODEV;
628 }
629
630 pdev = codec->dev;
631 while (strcmp(device_get_name(device_get_parent(pdev)), "pci") != 0) {
632 /* find the top-level PCI device handler */
633 pdev = device_get_parent(pdev);
634 }
635 codec->id = id;
636 codec->subvendor = (u_int32_t)pci_get_subdevice(pdev) << 16;
637 codec->subvendor |= (u_int32_t)pci_get_subvendor(pdev) &
638 0x0000ffff;
639 codec->noext = 0;
640 codec_patch = NULL;
641
642 cname = NULL;
643 for (i = 0; ac97codecid[i].id; i++) {
644 u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
645 if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
646 codec->noext = ac97codecid[i].noext;
647 codec_patch = ac97codecid[i].patch;
648 cname = ac97codecid[i].name;
649 break;
650 }
651 }
652
653 vname = NULL;
654 for (i = 0; ac97vendorid[i].id; i++) {
655 if (ac97vendorid[i].id == (id & 0xffffff00)) {
656 vname = ac97vendorid[i].name;
657 break;
658 }
659 }
660
661 codec->extcaps = 0;
662 codec->extid = 0;
663 codec->extstat = 0;
664 if (!codec->noext) {
665 i = ac97_rdcd(codec, AC97_REGEXT_ID);
666 if (i != 0xffff) {
667 codec->extcaps = i & 0x3fff;
668 codec->extid = (i & 0xc000) >> 14;
669 codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
670 }
671 }
672
673 for (i = 0; i < AC97_MIXER_SIZE; i++) {
674 codec->mix[i] = ac97mixtable_default[i];
675 }
676 ac97_fix_auxout(codec);
677 ac97_fix_tone(codec);
678 if (codec_patch)
679 codec_patch(codec);
680
681 for (i = 0; i < AC97_MIXER_SIZE; i++) {
682 k = codec->noext? codec->mix[i].enable : 1;
683 reg = codec->mix[i].reg;
684 if (reg < 0)
685 reg = -reg;
686 if (k && reg) {
687 j = old = ac97_rdcd(codec, reg);
688 /*
689 * Test for mute bit (except for AC97_MIX_TONE,
690 * where we simply assume it as available).
691 */
692 if (codec->mix[i].mute) {
693 ac97_wrcd(codec, reg, j | 0x8000);
694 j = ac97_rdcd(codec, reg);
695 } else
696 j |= 0x8000;
697 if ((j & 0x8000)) {
698 /*
699 * Test whether the control width should be
700 * 4, 5 or 6 bit. For 5bit register, we should
701 * test it whether it's really 5 or 6bit. Leave
702 * 4bit register alone, because sometimes an
703 * attempt to write past 4th bit may cause
704 * incorrect result especially for AC97_MIX_BEEP
705 * (ac97 2.3).
706 */
707 bit = codec->mix[i].bits;
708 if (bit == 5)
709 bit++;
710 j = ((1 << bit) - 1) << codec->mix[i].ofs;
711 ac97_wrcd(codec, reg,
712 j | (codec->mix[i].mute ? 0x8000 : 0));
713 k = ac97_rdcd(codec, reg) & j;
714 k >>= codec->mix[i].ofs;
715 if (reg == AC97_MIX_TONE &&
716 ((k & 0x0001) == 0x0000))
717 k >>= 1;
718 for (j = 0; k >> j; j++)
719 ;
720 if (j != 0) {
721 codec->mix[i].enable = 1;
722 codec->mix[i].bits = j;
723 } else if (reg == AC97_MIX_BEEP) {
724 /*
725 * Few codec such as CX20468-21 does
726 * have this control register, although
727 * the only usable part is the mute bit.
728 */
729 codec->mix[i].enable = 1;
730 } else
731 codec->mix[i].enable = 0;
732 } else
733 codec->mix[i].enable = 0;
734 ac97_wrcd(codec, reg, old);
735 }
736 }
737
738 device_printf(codec->dev, "<%s>\n",
739 ac97_hw_desc(codec->id, vname, cname, desc));
740
741 if (bootverbose) {
742 if (codec->flags & AC97_F_RDCD_BUG)
743 device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n");
744 device_printf(codec->dev, "Codec features ");
745 for (i = j = 0; i < 10; i++)
746 if (codec->caps & (1 << i))
747 printf("%s%s", j++? ", " : "", ac97feature[i]);
748 printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
749 printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
750
751 if (codec->extcaps != 0 || codec->extid) {
752 device_printf(codec->dev, "%s codec",
753 codec->extid? "Secondary" : "Primary");
754 if (codec->extcaps)
755 printf(" extended features ");
756 for (i = j = 0; i < 14; i++)
757 if (codec->extcaps & (1 << i))
758 printf("%s%s", j++? ", " : "", ac97extfeature[i]);
759 printf("\n");
760 }
761 }
762
763 i = 0;
764 while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) {
765 if (++i == 100) {
766 device_printf(codec->dev, "ac97 codec reports dac not ready\n");
767 break;
768 }
769 DELAY(1000);
770 }
771 if (bootverbose)
772 device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i);
773 mtx_unlock(&codec->lock);
774 return 0;
775 }
776
777 static unsigned
ac97_reinitmixer(struct ac97_info * codec)778 ac97_reinitmixer(struct ac97_info *codec)
779 {
780 mtx_lock(&codec->lock);
781 codec->count = AC97_INIT(codec->methods, codec->devinfo);
782 if (codec->count == 0) {
783 device_printf(codec->dev, "ac97 codec init failed\n");
784 mtx_unlock(&codec->lock);
785 return ENODEV;
786 }
787
788 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
789 ac97_reset(codec);
790 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
791
792 if (!codec->noext) {
793 ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
794 if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
795 != codec->extstat)
796 device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
797 codec->extstat,
798 ac97_rdcd(codec, AC97_REGEXT_STAT) &
799 AC97_EXTCAPS);
800 }
801
802 if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
803 device_printf(codec->dev, "ac97 codec reports dac not ready\n");
804 mtx_unlock(&codec->lock);
805 return 0;
806 }
807
808 struct ac97_info *
ac97_create(device_t dev,void * devinfo,kobj_class_t cls)809 ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
810 {
811 struct ac97_info *codec;
812 int i;
813
814 codec = malloc(sizeof(*codec), M_AC97, M_WAITOK | M_ZERO);
815 snprintf(codec->name, sizeof(codec->name), "%s:ac97",
816 device_get_nameunit(dev));
817 mtx_init(&codec->lock, codec->name, "ac97 codec", MTX_DEF);
818 codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO);
819 codec->dev = dev;
820 codec->devinfo = devinfo;
821 codec->flags = 0;
822
823 if (resource_int_value(device_get_name(dev), device_get_unit(dev),
824 "eapdinv", &i) == 0 && i != 0)
825 codec->flags |= AC97_F_EAPD_INV;
826
827 if (resource_int_value(device_get_name(dev), device_get_unit(dev),
828 "softpcmvol", &i) == 0 && i != 0)
829 pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL);
830
831 return codec;
832 }
833
834 void
ac97_destroy(struct ac97_info * codec)835 ac97_destroy(struct ac97_info *codec)
836 {
837 mtx_lock(&codec->lock);
838 if (codec->methods != NULL)
839 kobj_delete(codec->methods, M_AC97);
840 mtx_destroy(&codec->lock);
841 free(codec, M_AC97);
842 }
843
844 void
ac97_setflags(struct ac97_info * codec,u_int32_t val)845 ac97_setflags(struct ac97_info *codec, u_int32_t val)
846 {
847 codec->flags = val;
848 }
849
850 u_int32_t
ac97_getflags(struct ac97_info * codec)851 ac97_getflags(struct ac97_info *codec)
852 {
853 return codec->flags;
854 }
855
856 static void
ad1886_patch(struct ac97_info * codec)857 ad1886_patch(struct ac97_info *codec)
858 {
859 #define AC97_AD_JACK_SPDIF 0x72
860 /*
861 * Presario700 workaround
862 * for Jack Sense/SPDIF Register misetting causing
863 * no audible output
864 * by Santiago Nullo 04/05/2002
865 */
866 ac97_wrcd(codec, AC97_AD_JACK_SPDIF, 0x0010);
867 }
868
869 static void
ad198x_patch(struct ac97_info * codec)870 ad198x_patch(struct ac97_info *codec)
871 {
872 switch (ac97_getsubvendor(codec)) {
873 case 0x11931043: /* Not for ASUS A9T (probably else too). */
874 break;
875 default:
876 ac97_wrcd(codec, 0x76, ac97_rdcd(codec, 0x76) | 0x0420);
877 break;
878 }
879 }
880
881 static void
ad1981b_patch(struct ac97_info * codec)882 ad1981b_patch(struct ac97_info *codec)
883 {
884 /*
885 * Enable headphone jack sensing.
886 */
887 switch (ac97_getsubvendor(codec)) {
888 case 0x02d91014: /* IBM Thinkcentre */
889 case 0x099c103c: /* HP nx6110 */
890 ac97_wrcd(codec, AC97_AD_JACK_SPDIF,
891 ac97_rdcd(codec, AC97_AD_JACK_SPDIF) | 0x0800);
892 break;
893 default:
894 break;
895 }
896 }
897
898 static void
cmi9739_patch(struct ac97_info * codec)899 cmi9739_patch(struct ac97_info *codec)
900 {
901 /*
902 * Few laptops need extra register initialization
903 * to power up the internal speakers.
904 */
905 switch (ac97_getsubvendor(codec)) {
906 case 0x18431043: /* ASUS W1000N */
907 ac97_wrcd(codec, AC97_REG_POWER, 0x000f);
908 ac97_wrcd(codec, AC97_MIXEXT_CLFE, 0x0000);
909 ac97_wrcd(codec, 0x64, 0x7110);
910 break;
911 default:
912 break;
913 }
914 }
915
916 static void
alc655_patch(struct ac97_info * codec)917 alc655_patch(struct ac97_info *codec)
918 {
919 /*
920 * MSI (Micro-Star International) specific EAPD quirk.
921 */
922 switch (ac97_getsubvendor(codec)) {
923 case 0x00611462: /* MSI S250 */
924 case 0x01311462: /* MSI S270 */
925 case 0x01611462: /* LG K1 Express */
926 case 0x03511462: /* MSI L725 */
927 ac97_wrcd(codec, 0x7a, ac97_rdcd(codec, 0x7a) & 0xfffd);
928 break;
929 case 0x10ca1734:
930 /*
931 * Amilo Pro V2055 with ALC655 has phone out by default
932 * disabled (surround on), leaving us only with internal
933 * speakers. This should really go to mixer. We write the
934 * Data Flow Control reg.
935 */
936 ac97_wrcd(codec, 0x6a, ac97_rdcd(codec, 0x6a) | 0x0001);
937 break;
938 default:
939 break;
940 }
941 }
942
943 /* -------------------------------------------------------------------- */
944
945 static int
sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS)946 sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS)
947 {
948 struct ac97_info *codec;
949 int ea, inv, err = 0;
950 u_int16_t val;
951
952 codec = oidp->oid_arg1;
953 if (codec == NULL || codec->id == 0)
954 return EINVAL;
955 mtx_lock(&codec->lock);
956 val = ac97_rdcd(codec, AC97_REG_POWER);
957 inv = (codec->flags & AC97_F_EAPD_INV) ? 0 : 1;
958 ea = (val >> 15) ^ inv;
959 mtx_unlock(&codec->lock);
960 err = sysctl_handle_int(oidp, &ea, 0, req);
961 if (err == 0 && req->newptr != NULL) {
962 if (ea != 0 && ea != 1)
963 return EINVAL;
964 if (ea != ((val >> 15) ^ inv)) {
965 mtx_lock(&codec->lock);
966 ac97_wrcd(codec, AC97_REG_POWER, val ^ 0x8000);
967 mtx_unlock(&codec->lock);
968 }
969 }
970 return err;
971 }
972
973 static void
ac97_init_sysctl(struct ac97_info * codec)974 ac97_init_sysctl(struct ac97_info *codec)
975 {
976 u_int16_t orig, val;
977
978 if (codec == NULL || codec->dev == NULL)
979 return;
980 mtx_lock(&codec->lock);
981 orig = ac97_rdcd(codec, AC97_REG_POWER);
982 ac97_wrcd(codec, AC97_REG_POWER, orig ^ 0x8000);
983 val = ac97_rdcd(codec, AC97_REG_POWER);
984 ac97_wrcd(codec, AC97_REG_POWER, orig);
985 mtx_unlock(&codec->lock);
986 if ((val & 0x8000) == (orig & 0x8000))
987 return;
988 SYSCTL_ADD_PROC(device_get_sysctl_ctx(codec->dev),
989 SYSCTL_CHILDREN(device_get_sysctl_tree(codec->dev)),
990 OID_AUTO, "eapd",
991 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
992 codec, sizeof(codec), sysctl_hw_snd_ac97_eapd,
993 "I", "AC97 External Amplifier");
994 }
995
996 static int
ac97mix_init(struct snd_mixer * m)997 ac97mix_init(struct snd_mixer *m)
998 {
999 struct ac97_info *codec = mix_getdevinfo(m);
1000 u_int32_t i, mask;
1001
1002 if (codec == NULL)
1003 return -1;
1004
1005 if (ac97_initmixer(codec))
1006 return -1;
1007
1008 switch (codec->id) {
1009 case 0x41445374: /* AD1981B */
1010 switch (codec->subvendor) {
1011 case 0x02d91014:
1012 /*
1013 * IBM Thinkcentre:
1014 *
1015 * Tie "ogain" and "phout" to "vol" since its
1016 * master volume is basically useless and can't
1017 * control anything.
1018 */
1019 mask = 0;
1020 if (codec->mix[SOUND_MIXER_OGAIN].enable)
1021 mask |= SOUND_MASK_OGAIN;
1022 if (codec->mix[SOUND_MIXER_PHONEOUT].enable)
1023 mask |= SOUND_MASK_PHONEOUT;
1024 if (codec->mix[SOUND_MIXER_VOLUME].enable)
1025 mix_setparentchild(m, SOUND_MIXER_VOLUME,
1026 mask);
1027 else {
1028 mix_setparentchild(m, SOUND_MIXER_VOLUME,
1029 mask);
1030 mix_setrealdev(m, SOUND_MIXER_VOLUME,
1031 SOUND_MIXER_NONE);
1032 }
1033 break;
1034 case 0x099c103c:
1035 /*
1036 * HP nx6110:
1037 *
1038 * By default, "vol" is controlling internal speakers
1039 * (not a master volume!) and "ogain" is controlling
1040 * headphone. Enable dummy "phout" so it can be
1041 * remapped to internal speakers and virtualize
1042 * "vol" to control both.
1043 */
1044 codec->mix[SOUND_MIXER_OGAIN].enable = 1;
1045 codec->mix[SOUND_MIXER_PHONEOUT].enable = 1;
1046 mix_setrealdev(m, SOUND_MIXER_PHONEOUT,
1047 SOUND_MIXER_VOLUME);
1048 mix_setrealdev(m, SOUND_MIXER_VOLUME,
1049 SOUND_MIXER_NONE);
1050 mix_setparentchild(m, SOUND_MIXER_VOLUME,
1051 SOUND_MASK_OGAIN | SOUND_MASK_PHONEOUT);
1052 break;
1053 default:
1054 break;
1055 }
1056 break;
1057 case 0x434d4941: /* CMI9738 */
1058 case 0x434d4961: /* CMI9739 */
1059 case 0x434d4978: /* CMI9761 */
1060 case 0x434d4982: /* CMI9761 */
1061 case 0x434d4983: /* CMI9761 */
1062 bzero(&codec->mix[SOUND_MIXER_PCM],
1063 sizeof(codec->mix[SOUND_MIXER_PCM]));
1064 pcm_setflags(codec->dev, pcm_getflags(codec->dev) |
1065 SD_F_SOFTPCMVOL);
1066 /* XXX How about master volume ? */
1067 break;
1068 default:
1069 break;
1070 }
1071
1072 if (pcm_getflags(codec->dev) & SD_F_SOFTPCMVOL)
1073 ac97_wrcd(codec, AC97_MIX_PCM, 0);
1074
1075 mask = 0;
1076 for (i = 0; i < AC97_MIXER_SIZE; i++)
1077 mask |= codec->mix[i].enable? 1 << i : 0;
1078 mix_setdevs(m, mask);
1079
1080 mask = 0;
1081 for (i = 0; i < AC97_MIXER_SIZE; i++)
1082 mask |= codec->mix[i].recidx? 1 << i : 0;
1083 mix_setrecdevs(m, mask);
1084
1085 ac97_init_sysctl(codec);
1086
1087 return 0;
1088 }
1089
1090 static int
ac97mix_uninit(struct snd_mixer * m)1091 ac97mix_uninit(struct snd_mixer *m)
1092 {
1093 struct ac97_info *codec = mix_getdevinfo(m);
1094
1095 if (codec == NULL)
1096 return -1;
1097 ac97_destroy(codec);
1098 return 0;
1099 }
1100
1101 static int
ac97mix_reinit(struct snd_mixer * m)1102 ac97mix_reinit(struct snd_mixer *m)
1103 {
1104 struct ac97_info *codec = mix_getdevinfo(m);
1105
1106 if (codec == NULL)
1107 return -1;
1108 return ac97_reinitmixer(codec);
1109 }
1110
1111 static int
ac97mix_set(struct snd_mixer * m,unsigned dev,unsigned left,unsigned right)1112 ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
1113 {
1114 struct ac97_info *codec = mix_getdevinfo(m);
1115
1116 if (codec == NULL || dev >= AC97_MIXER_SIZE)
1117 return -1;
1118 return ac97_setmixer(codec, dev, left, right);
1119 }
1120
1121 static u_int32_t
ac97mix_setrecsrc(struct snd_mixer * m,u_int32_t src)1122 ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
1123 {
1124 int i;
1125 struct ac97_info *codec = mix_getdevinfo(m);
1126
1127 if (codec == NULL)
1128 return -1;
1129 for (i = 0; i < AC97_MIXER_SIZE; i++)
1130 if ((src & (1 << i)) != 0)
1131 break;
1132 return (ac97_setrecsrc(codec, i) == 0)? 1U << i : 0xffffffffU;
1133 }
1134
1135 static kobj_method_t ac97mixer_methods[] = {
1136 KOBJMETHOD(mixer_init, ac97mix_init),
1137 KOBJMETHOD(mixer_uninit, ac97mix_uninit),
1138 KOBJMETHOD(mixer_reinit, ac97mix_reinit),
1139 KOBJMETHOD(mixer_set, ac97mix_set),
1140 KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc),
1141 KOBJMETHOD_END
1142 };
1143 MIXER_DECLARE(ac97mixer);
1144
1145 /* -------------------------------------------------------------------- */
1146
1147 kobj_class_t
ac97_getmixerclass(void)1148 ac97_getmixerclass(void)
1149 {
1150 return &ac97mixer_class;
1151 }
1152