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 (!is_pci_device(pdev)) {
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 kobj_delete(codec->methods, M_AC97);
839 mtx_destroy(&codec->lock);
840 free(codec, M_AC97);
841 }
842
843 void
ac97_setflags(struct ac97_info * codec,u_int32_t val)844 ac97_setflags(struct ac97_info *codec, u_int32_t val)
845 {
846 codec->flags = val;
847 }
848
849 u_int32_t
ac97_getflags(struct ac97_info * codec)850 ac97_getflags(struct ac97_info *codec)
851 {
852 return codec->flags;
853 }
854
855 static void
ad1886_patch(struct ac97_info * codec)856 ad1886_patch(struct ac97_info *codec)
857 {
858 #define AC97_AD_JACK_SPDIF 0x72
859 /*
860 * Presario700 workaround
861 * for Jack Sense/SPDIF Register misetting causing
862 * no audible output
863 * by Santiago Nullo 04/05/2002
864 */
865 ac97_wrcd(codec, AC97_AD_JACK_SPDIF, 0x0010);
866 }
867
868 static void
ad198x_patch(struct ac97_info * codec)869 ad198x_patch(struct ac97_info *codec)
870 {
871 switch (ac97_getsubvendor(codec)) {
872 case 0x11931043: /* Not for ASUS A9T (probably else too). */
873 break;
874 default:
875 ac97_wrcd(codec, 0x76, ac97_rdcd(codec, 0x76) | 0x0420);
876 break;
877 }
878 }
879
880 static void
ad1981b_patch(struct ac97_info * codec)881 ad1981b_patch(struct ac97_info *codec)
882 {
883 /*
884 * Enable headphone jack sensing.
885 */
886 switch (ac97_getsubvendor(codec)) {
887 case 0x02d91014: /* IBM Thinkcentre */
888 case 0x099c103c: /* HP nx6110 */
889 ac97_wrcd(codec, AC97_AD_JACK_SPDIF,
890 ac97_rdcd(codec, AC97_AD_JACK_SPDIF) | 0x0800);
891 break;
892 default:
893 break;
894 }
895 }
896
897 static void
cmi9739_patch(struct ac97_info * codec)898 cmi9739_patch(struct ac97_info *codec)
899 {
900 /*
901 * Few laptops need extra register initialization
902 * to power up the internal speakers.
903 */
904 switch (ac97_getsubvendor(codec)) {
905 case 0x18431043: /* ASUS W1000N */
906 ac97_wrcd(codec, AC97_REG_POWER, 0x000f);
907 ac97_wrcd(codec, AC97_MIXEXT_CLFE, 0x0000);
908 ac97_wrcd(codec, 0x64, 0x7110);
909 break;
910 default:
911 break;
912 }
913 }
914
915 static void
alc655_patch(struct ac97_info * codec)916 alc655_patch(struct ac97_info *codec)
917 {
918 /*
919 * MSI (Micro-Star International) specific EAPD quirk.
920 */
921 switch (ac97_getsubvendor(codec)) {
922 case 0x00611462: /* MSI S250 */
923 case 0x01311462: /* MSI S270 */
924 case 0x01611462: /* LG K1 Express */
925 case 0x03511462: /* MSI L725 */
926 ac97_wrcd(codec, 0x7a, ac97_rdcd(codec, 0x7a) & 0xfffd);
927 break;
928 case 0x10ca1734:
929 /*
930 * Amilo Pro V2055 with ALC655 has phone out by default
931 * disabled (surround on), leaving us only with internal
932 * speakers. This should really go to mixer. We write the
933 * Data Flow Control reg.
934 */
935 ac97_wrcd(codec, 0x6a, ac97_rdcd(codec, 0x6a) | 0x0001);
936 break;
937 default:
938 break;
939 }
940 }
941
942 /* -------------------------------------------------------------------- */
943
944 static int
sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS)945 sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS)
946 {
947 struct ac97_info *codec;
948 int ea, inv, err = 0;
949 u_int16_t val;
950
951 codec = oidp->oid_arg1;
952 if (codec == NULL || codec->id == 0)
953 return EINVAL;
954 mtx_lock(&codec->lock);
955 val = ac97_rdcd(codec, AC97_REG_POWER);
956 inv = (codec->flags & AC97_F_EAPD_INV) ? 0 : 1;
957 ea = (val >> 15) ^ inv;
958 mtx_unlock(&codec->lock);
959 err = sysctl_handle_int(oidp, &ea, 0, req);
960 if (err == 0 && req->newptr != NULL) {
961 if (ea != 0 && ea != 1)
962 return EINVAL;
963 if (ea != ((val >> 15) ^ inv)) {
964 mtx_lock(&codec->lock);
965 ac97_wrcd(codec, AC97_REG_POWER, val ^ 0x8000);
966 mtx_unlock(&codec->lock);
967 }
968 }
969 return err;
970 }
971
972 static void
ac97_init_sysctl(struct ac97_info * codec)973 ac97_init_sysctl(struct ac97_info *codec)
974 {
975 u_int16_t orig, val;
976
977 if (codec == NULL || codec->dev == NULL)
978 return;
979 mtx_lock(&codec->lock);
980 orig = ac97_rdcd(codec, AC97_REG_POWER);
981 ac97_wrcd(codec, AC97_REG_POWER, orig ^ 0x8000);
982 val = ac97_rdcd(codec, AC97_REG_POWER);
983 ac97_wrcd(codec, AC97_REG_POWER, orig);
984 mtx_unlock(&codec->lock);
985 if ((val & 0x8000) == (orig & 0x8000))
986 return;
987 SYSCTL_ADD_PROC(device_get_sysctl_ctx(codec->dev),
988 SYSCTL_CHILDREN(device_get_sysctl_tree(codec->dev)),
989 OID_AUTO, "eapd",
990 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
991 codec, sizeof(codec), sysctl_hw_snd_ac97_eapd,
992 "I", "AC97 External Amplifier");
993 }
994
995 static int
ac97mix_init(struct snd_mixer * m)996 ac97mix_init(struct snd_mixer *m)
997 {
998 struct ac97_info *codec = mix_getdevinfo(m);
999 u_int32_t i, mask;
1000
1001 if (codec == NULL)
1002 return -1;
1003
1004 if (ac97_initmixer(codec))
1005 return -1;
1006
1007 switch (codec->id) {
1008 case 0x41445374: /* AD1981B */
1009 switch (codec->subvendor) {
1010 case 0x02d91014:
1011 /*
1012 * IBM Thinkcentre:
1013 *
1014 * Tie "ogain" and "phout" to "vol" since its
1015 * master volume is basically useless and can't
1016 * control anything.
1017 */
1018 mask = 0;
1019 if (codec->mix[SOUND_MIXER_OGAIN].enable)
1020 mask |= SOUND_MASK_OGAIN;
1021 if (codec->mix[SOUND_MIXER_PHONEOUT].enable)
1022 mask |= SOUND_MASK_PHONEOUT;
1023 if (codec->mix[SOUND_MIXER_VOLUME].enable)
1024 mix_setparentchild(m, SOUND_MIXER_VOLUME,
1025 mask);
1026 else {
1027 mix_setparentchild(m, SOUND_MIXER_VOLUME,
1028 mask);
1029 mix_setrealdev(m, SOUND_MIXER_VOLUME,
1030 SOUND_MIXER_NONE);
1031 }
1032 break;
1033 case 0x099c103c:
1034 /*
1035 * HP nx6110:
1036 *
1037 * By default, "vol" is controlling internal speakers
1038 * (not a master volume!) and "ogain" is controlling
1039 * headphone. Enable dummy "phout" so it can be
1040 * remapped to internal speakers and virtualize
1041 * "vol" to control both.
1042 */
1043 codec->mix[SOUND_MIXER_OGAIN].enable = 1;
1044 codec->mix[SOUND_MIXER_PHONEOUT].enable = 1;
1045 mix_setrealdev(m, SOUND_MIXER_PHONEOUT,
1046 SOUND_MIXER_VOLUME);
1047 mix_setrealdev(m, SOUND_MIXER_VOLUME,
1048 SOUND_MIXER_NONE);
1049 mix_setparentchild(m, SOUND_MIXER_VOLUME,
1050 SOUND_MASK_OGAIN | SOUND_MASK_PHONEOUT);
1051 break;
1052 default:
1053 break;
1054 }
1055 break;
1056 case 0x434d4941: /* CMI9738 */
1057 case 0x434d4961: /* CMI9739 */
1058 case 0x434d4978: /* CMI9761 */
1059 case 0x434d4982: /* CMI9761 */
1060 case 0x434d4983: /* CMI9761 */
1061 bzero(&codec->mix[SOUND_MIXER_PCM],
1062 sizeof(codec->mix[SOUND_MIXER_PCM]));
1063 pcm_setflags(codec->dev, pcm_getflags(codec->dev) |
1064 SD_F_SOFTPCMVOL);
1065 /* XXX How about master volume ? */
1066 break;
1067 default:
1068 break;
1069 }
1070
1071 if (pcm_getflags(codec->dev) & SD_F_SOFTPCMVOL)
1072 ac97_wrcd(codec, AC97_MIX_PCM, 0);
1073
1074 mask = 0;
1075 for (i = 0; i < AC97_MIXER_SIZE; i++)
1076 mask |= codec->mix[i].enable? 1 << i : 0;
1077 mix_setdevs(m, mask);
1078
1079 mask = 0;
1080 for (i = 0; i < AC97_MIXER_SIZE; i++)
1081 mask |= codec->mix[i].recidx? 1 << i : 0;
1082 mix_setrecdevs(m, mask);
1083
1084 ac97_init_sysctl(codec);
1085
1086 return 0;
1087 }
1088
1089 static int
ac97mix_uninit(struct snd_mixer * m)1090 ac97mix_uninit(struct snd_mixer *m)
1091 {
1092 struct ac97_info *codec = mix_getdevinfo(m);
1093
1094 if (codec == NULL)
1095 return -1;
1096 ac97_destroy(codec);
1097 return 0;
1098 }
1099
1100 static int
ac97mix_reinit(struct snd_mixer * m)1101 ac97mix_reinit(struct snd_mixer *m)
1102 {
1103 struct ac97_info *codec = mix_getdevinfo(m);
1104
1105 if (codec == NULL)
1106 return -1;
1107 return ac97_reinitmixer(codec);
1108 }
1109
1110 static int
ac97mix_set(struct snd_mixer * m,unsigned dev,unsigned left,unsigned right)1111 ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
1112 {
1113 struct ac97_info *codec = mix_getdevinfo(m);
1114
1115 if (codec == NULL || dev >= AC97_MIXER_SIZE)
1116 return -1;
1117 return ac97_setmixer(codec, dev, left, right);
1118 }
1119
1120 static u_int32_t
ac97mix_setrecsrc(struct snd_mixer * m,u_int32_t src)1121 ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
1122 {
1123 int i;
1124 struct ac97_info *codec = mix_getdevinfo(m);
1125
1126 if (codec == NULL)
1127 return -1;
1128 for (i = 0; i < AC97_MIXER_SIZE; i++)
1129 if ((src & (1 << i)) != 0)
1130 break;
1131 return (ac97_setrecsrc(codec, i) == 0)? 1U << i : 0xffffffffU;
1132 }
1133
1134 static kobj_method_t ac97mixer_methods[] = {
1135 KOBJMETHOD(mixer_init, ac97mix_init),
1136 KOBJMETHOD(mixer_uninit, ac97mix_uninit),
1137 KOBJMETHOD(mixer_reinit, ac97mix_reinit),
1138 KOBJMETHOD(mixer_set, ac97mix_set),
1139 KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc),
1140 KOBJMETHOD_END
1141 };
1142 MIXER_DECLARE(ac97mixer);
1143
1144 /* -------------------------------------------------------------------- */
1145
1146 kobj_class_t
ac97_getmixerclass(void)1147 ac97_getmixerclass(void)
1148 {
1149 return &ac97mixer_class;
1150 }
1151