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