xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision b52b9d56d4e96089873a75f9e29062eec19fabba)
1 /*
2  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <dev/sound/pcm/sound.h>
28 #include <dev/sound/pcm/ac97.h>
29 
30 #include "mixer_if.h"
31 
32 SND_DECLARE_FILE("$FreeBSD$");
33 
34 MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
35 
36 struct ac97mixtable_entry {
37 	int		reg:8;
38 	unsigned	bits:4;
39 	unsigned	ofs:4;
40 	unsigned	stereo:1;
41 	unsigned	mute:1;
42 	unsigned	recidx:4;
43 	unsigned        mask:1;
44 	unsigned	enable:1;
45 };
46 
47 #define AC97_NAMELEN	16
48 struct ac97_info {
49 	kobj_t methods;
50 	device_t dev;
51 	void *devinfo;
52 	char *id;
53 	char rev;
54 	unsigned count, caps, se, extcaps, extid, extstat, noext:1;
55 	u_int32_t flags;
56 	struct ac97mixtable_entry mix[32];
57 	char name[AC97_NAMELEN];
58 	void *lock;
59 };
60 
61 struct ac97_codecid {
62 	u_int32_t id, noext:1;
63 	char *name;
64 };
65 
66 static const struct ac97mixtable_entry ac97mixtable_default[32] = {
67 	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0, 1 },
68 	[SOUND_MIXER_MONITOR]	= { AC97_MIX_PHONES, 	5, 0, 1, 1, 0, 0, 0 },
69 	[SOUND_MIXER_PHONEOUT]	= { AC97_MIX_MONO, 	5, 0, 0, 1, 7, 0, 0 },
70 	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1, 0 },
71 	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1, 0 },
72 	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0, 1 },
73 	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0, 0 },
74 	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0, 1 },
75 	[SOUND_MIXER_PHONEIN]	= { AC97_MIX_PHONE, 	5, 0, 0, 1, 8, 0, 0 },
76 	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 0, 1 },
77 	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
78 	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
79 	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
80 	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
81 };
82 
83 static struct ac97_codecid ac97codecid[] = {
84 	{ 0x41445303, 0, "Analog Devices AD1819" },
85 	{ 0x41445340, 0, "Analog Devices AD1881" },
86 	{ 0x41445348, 0, "Analog Devices AD1881A" },
87 	{ 0x41445360, 0, "Analog Devices AD1885" },
88 	{ 0x414b4d00, 1, "Asahi Kasei AK4540" },
89 	{ 0x414b4d01, 1, "Asahi Kasei AK4542" },
90 	{ 0x414b4d02, 1, "Asahi Kasei AK4543" },
91 	{ 0x414c4710, 0, "Avance Logic ALC200/200P" },
92 	{ 0x43525900, 0, "Cirrus Logic CS4297" },
93 	{ 0x43525903, 0, "Cirrus Logic CS4297" },
94 	{ 0x43525913, 0, "Cirrus Logic CS4297A" },
95 	{ 0x43525914, 0, "Cirrus Logic CS4297B" },
96 	{ 0x43525923, 0, "Cirrus Logic CS4294C" },
97 	{ 0x4352592b, 0, "Cirrus Logic CS4298C" },
98 	{ 0x43525931, 0, "Cirrus Logic CS4299A" },
99 	{ 0x43525933, 0, "Cirrus Logic CS4299C" },
100 	{ 0x43525934, 0, "Cirrus Logic CS4299D" },
101 	{ 0x43525941, 0, "Cirrus Logic CS4201A" },
102 	{ 0x43525951, 0, "Cirrus Logic CS4205A" },
103 	{ 0x43525961, 0, "Cirrus Logic CS4291A" },
104 	{ 0x45838308, 0, "ESS Technology ES1921" },
105 	{ 0x49434511, 0, "ICEnsemble ICE1232" },
106 	{ 0x4e534331, 0, "National Semiconductor LM4549" },
107 	{ 0x83847600, 0, "SigmaTel STAC9700/9783/9784" },
108 	{ 0x83847604, 0, "SigmaTel STAC9701/9703/9704/9705" },
109 	{ 0x83847605, 0, "SigmaTel STAC9704" },
110 	{ 0x83847608, 0, "SigmaTel STAC9708/9711" },
111 	{ 0x83847609, 0, "SigmaTel STAC9721/9723" },
112 	{ 0x83847644, 0, "SigmaTel STAC9744" },
113 	{ 0x83847656, 0, "SigmaTel STAC9756/9757" },
114 	{ 0x53494c22, 0, "Silicon Laboratory Si3036" },
115 	{ 0x53494c23, 0, "Silicon Laboratory Si3038" },
116 	{ 0x54524103, 0, "TriTech TR?????" },
117 	{ 0x54524106, 0, "TriTech TR28026" },
118 	{ 0x54524108, 0, "TriTech TR28028" },
119 	{ 0x54524123, 0, "TriTech TR28602" },
120 	{ 0x574d4c00, 0, "Wolfson WM9701A" },
121 	{ 0x574d4c03, 0, "Wolfson WM9703/9704" },
122 	{ 0x574d4c04, 0, "Wolfson WM9704 (quad)" },
123 	{ 0, 0, NULL }
124 };
125 
126 static char *ac97enhancement[] = {
127 	"no 3D Stereo Enhancement",
128 	"Analog Devices Phat Stereo",
129 	"Creative Stereo Enhancement",
130 	"National Semi 3D Stereo Enhancement",
131 	"Yamaha Ymersion",
132 	"BBE 3D Stereo Enhancement",
133 	"Crystal Semi 3D Stereo Enhancement",
134 	"Qsound QXpander",
135 	"Spatializer 3D Stereo Enhancement",
136 	"SRS 3D Stereo Enhancement",
137 	"Platform Tech 3D Stereo Enhancement",
138 	"AKM 3D Audio",
139 	"Aureal Stereo Enhancement",
140 	"Aztech 3D Enhancement",
141 	"Binaura 3D Audio Enhancement",
142 	"ESS Technology Stereo Enhancement",
143 	"Harman International VMAx",
144 	"Nvidea 3D Stereo Enhancement",
145 	"Philips Incredible Sound",
146 	"Texas Instruments 3D Stereo Enhancement",
147 	"VLSI Technology 3D Stereo Enhancement",
148 	"TriTech 3D Stereo Enhancement",
149 	"Realtek 3D Stereo Enhancement",
150 	"Samsung 3D Stereo Enhancement",
151 	"Wolfson Microelectronics 3D Enhancement",
152 	"Delta Integration 3D Enhancement",
153 	"SigmaTel 3D Enhancement",
154 	"Reserved 27",
155 	"Rockwell 3D Stereo Enhancement",
156 	"Reserved 29",
157 	"Reserved 30",
158 	"Reserved 31"
159 };
160 
161 static char *ac97feature[] = {
162 	"mic channel",
163 	"reserved",
164 	"tone",
165 	"simulated stereo",
166 	"headphone",
167 	"bass boost",
168 	"18 bit DAC",
169 	"20 bit DAC",
170 	"18 bit ADC",
171 	"20 bit ADC"
172 };
173 
174 static char *ac97extfeature[] = {
175 	"variable rate PCM",
176 	"double rate PCM",
177 	"reserved 1",
178 	"variable rate mic",
179 	"reserved 2",
180 	"reserved 3",
181 	"center DAC",
182 	"surround DAC",
183 	"LFE DAC",
184 	"AMAP",
185 	"reserved 4",
186 	"reserved 5",
187 	"reserved 6",
188 	"reserved 7",
189 };
190 
191 static u_int16_t
192 rdcd(struct ac97_info *codec, int reg)
193 {
194 	return AC97_READ(codec->methods, codec->devinfo, reg);
195 }
196 
197 static void
198 wrcd(struct ac97_info *codec, int reg, u_int16_t val)
199 {
200 	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
201 }
202 
203 static void
204 ac97_reset(struct ac97_info *codec)
205 {
206 	u_int32_t i, ps;
207 	wrcd(codec, AC97_REG_RESET, 0);
208 	for (i = 0; i < 500; i++) {
209 		ps = rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
210 		if (ps == AC97_POWER_STATUS)
211 			return;
212 		DELAY(1000);
213 	}
214 	device_printf(codec->dev, "AC97 reset timed out.");
215 }
216 
217 int
218 ac97_setrate(struct ac97_info *codec, int which, int rate)
219 {
220 	u_int16_t v;
221 
222 	switch(which) {
223 	case AC97_REGEXT_FDACRATE:
224 	case AC97_REGEXT_SDACRATE:
225 	case AC97_REGEXT_LDACRATE:
226 	case AC97_REGEXT_LADCRATE:
227 	case AC97_REGEXT_MADCRATE:
228 		break;
229 
230 	default:
231 		return -1;
232 	}
233 
234 	snd_mtxlock(codec->lock);
235 	if (rate != 0) {
236 		v = rate;
237 		if (codec->extstat & AC97_EXTCAP_DRA)
238 			v >>= 1;
239 		wrcd(codec, which, v);
240 	}
241 	v = rdcd(codec, which);
242 	if (codec->extstat & AC97_EXTCAP_DRA)
243 		v <<= 1;
244 	snd_mtxunlock(codec->lock);
245 	return v;
246 }
247 
248 int
249 ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
250 {
251 	mode &= AC97_EXTCAPS;
252 	if ((mode & ~codec->extcaps) != 0) {
253 		device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
254 			      mode);
255 		return -1;
256 	}
257 	snd_mtxlock(codec->lock);
258 	wrcd(codec, AC97_REGEXT_STAT, mode);
259 	codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
260 	snd_mtxunlock(codec->lock);
261 	return (mode == codec->extstat)? 0 : -1;
262 }
263 
264 u_int16_t
265 ac97_getextmode(struct ac97_info *codec)
266 {
267 	return codec->extstat;
268 }
269 
270 u_int16_t
271 ac97_getextcaps(struct ac97_info *codec)
272 {
273 	return codec->extcaps;
274 }
275 
276 u_int16_t
277 ac97_getcaps(struct ac97_info *codec)
278 {
279 	return codec->caps;
280 }
281 
282 static int
283 ac97_setrecsrc(struct ac97_info *codec, int channel)
284 {
285 	struct ac97mixtable_entry *e = &codec->mix[channel];
286 
287 	if (e->recidx > 0) {
288 		int val = e->recidx - 1;
289 		val |= val << 8;
290 		snd_mtxlock(codec->lock);
291 		wrcd(codec, AC97_REG_RECSEL, val);
292 		snd_mtxunlock(codec->lock);
293 		return 0;
294 	} else
295 		return -1;
296 }
297 
298 static int
299 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
300 {
301 	struct ac97mixtable_entry *e = &codec->mix[channel];
302 
303 	if (e->reg && e->enable && e->bits) {
304 		int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
305 
306 		if (!e->stereo)
307 			right = left;
308 		if (e->reg > 0) {
309 			left = 100 - left;
310 			right = 100 - right;
311 		}
312 
313 		max = (1 << e->bits) - 1;
314 		left = (left * max) / 100;
315 		right = (right * max) / 100;
316 
317 		val = (left << 8) | right;
318 
319 		left = (left * 100) / max;
320 		right = (right * 100) / max;
321 
322 		if (e->reg > 0) {
323 			left = 100 - left;
324 			right = 100 - right;
325 		}
326 
327 		if (!e->stereo) {
328 			val &= max;
329 			val <<= e->ofs;
330 			if (e->mask) {
331 				int cur = rdcd(codec, e->reg);
332 				val |= cur & ~(max << e->ofs);
333 			}
334 		}
335 		if (left == 0 && right == 0 && e->mute == 1)
336 			val = AC97_MUTE;
337 		snd_mtxlock(codec->lock);
338 		wrcd(codec, reg, val);
339 		snd_mtxunlock(codec->lock);
340 		return left | (right << 8);
341 	} else {
342 		/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
343 		return -1;
344 	}
345 }
346 
347 #if 0
348 static int
349 ac97_getmixer(struct ac97_info *codec, int channel)
350 {
351 	struct ac97mixtable_entry *e = &codec->mix[channel];
352 	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
353 		int max, val, volume;
354 
355 		max = (1 << e->bits) - 1;
356 		val = rdcd(code, e->reg);
357 		if (val == AC97_MUTE && e->mute == 1)
358 			volume = 0;
359 		else {
360 			if (e->stereo == 0) val >>= e->ofs;
361 			val &= max;
362 			volume = (val * 100) / max;
363 			if (e->reg > 0) volume = 100 - volume;
364 		}
365 		return volume;
366 	} else
367 		return -1;
368 }
369 #endif
370 
371 static unsigned
372 ac97_initmixer(struct ac97_info *codec)
373 {
374 	unsigned i, j, k, old;
375 	u_int32_t id;
376 
377 	snd_mtxlock(codec->lock);
378 	for (i = 0; i < 32; i++)
379 		codec->mix[i] = ac97mixtable_default[i];
380 
381 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
382 	if (codec->count == 0) {
383 		device_printf(codec->dev, "ac97 codec init failed\n");
384 		snd_mtxunlock(codec->lock);
385 		return ENODEV;
386 	}
387 
388 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
389 	ac97_reset(codec);
390 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
391 
392 	i = rdcd(codec, AC97_REG_RESET);
393 	codec->caps = i & 0x03ff;
394 	codec->se =  (i & 0x7c00) >> 10;
395 
396 	id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2);
397 	codec->rev = id & 0x000000ff;
398 	if (id == 0 || id == 0xffffffff) {
399 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
400 		snd_mtxunlock(codec->lock);
401 		return ENODEV;
402 	}
403 
404 	codec->noext = 0;
405 	codec->id = NULL;
406 	for (i = 0; ac97codecid[i].id; i++) {
407 		if (ac97codecid[i].id == id) {
408 			codec->id = ac97codecid[i].name;
409 			codec->noext = ac97codecid[i].noext;
410 		}
411 	}
412 
413 	codec->extcaps = 0;
414 	codec->extid = 0;
415 	codec->extstat = 0;
416 	if (!codec->noext) {
417 		i = rdcd(codec, AC97_REGEXT_ID);
418 		if (i != 0xffff) {
419 			codec->extcaps = i & 0x3fff;
420 			codec->extid =  (i & 0xc000) >> 14;
421 			codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
422 		}
423 	}
424 
425 	for (i = 0; i < 32; i++) {
426 		k = codec->noext? codec->mix[i].enable : 1;
427 		if (k && (codec->mix[i].reg > 0)) {
428 			old = rdcd(codec, codec->mix[i].reg);
429 			wrcd(codec, codec->mix[i].reg, 0x3f);
430 			j = rdcd(codec, codec->mix[i].reg);
431 			wrcd(codec, codec->mix[i].reg, old);
432 			codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
433 			for (k = 1; j & (1 << k); k++);
434 			codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
435 		}
436 		/* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
437 	}
438 
439 	if (bootverbose) {
440 		device_printf(codec->dev, "ac97 codec id 0x%08x", id);
441 		if (codec->id)
442 			printf(" (%s)", codec->id);
443 		printf("\n");
444 		device_printf(codec->dev, "ac97 codec features ");
445 		for (i = j = 0; i < 10; i++)
446 			if (codec->caps & (1 << i))
447 				printf("%s%s", j++? ", " : "", ac97feature[i]);
448 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
449 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
450 
451 		if (codec->extcaps != 0 || codec->extid) {
452 			device_printf(codec->dev, "ac97 %s codec",
453 				      codec->extid? "secondary" : "primary");
454 			if (codec->extcaps)
455 				printf(" extended features ");
456 			for (i = j = 0; i < 14; i++)
457 				if (codec->extcaps & (1 << i))
458 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
459 			printf("\n");
460 		}
461 	}
462 
463 	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
464 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
465 	snd_mtxunlock(codec->lock);
466 	return 0;
467 }
468 
469 static unsigned
470 ac97_reinitmixer(struct ac97_info *codec)
471 {
472 	snd_mtxlock(codec->lock);
473 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
474 	if (codec->count == 0) {
475 		device_printf(codec->dev, "ac97 codec init failed\n");
476 		snd_mtxunlock(codec->lock);
477 		return ENODEV;
478 	}
479 
480 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
481 	ac97_reset(codec);
482 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
483 
484 	if (!codec->noext) {
485 		wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
486 		if ((rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
487 		    != codec->extstat)
488 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
489 				      codec->extstat,
490 				      rdcd(codec, AC97_REGEXT_STAT) &
491 					AC97_EXTCAPS);
492 	}
493 
494 	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
495 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
496 	snd_mtxunlock(codec->lock);
497 	return 0;
498 }
499 
500 struct ac97_info *
501 ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
502 {
503 	struct ac97_info *codec;
504 
505 	codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
506 	if (codec == NULL)
507 		return NULL;
508 
509 	snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
510 	codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
511 	codec->methods = kobj_create(cls, M_AC97, M_WAITOK);
512 	if (codec->methods == NULL) {
513 		snd_mtxlock(codec->lock);
514 		snd_mtxfree(codec->lock);
515 		free(codec, M_AC97);
516 		return NULL;
517 	}
518 
519 	codec->dev = dev;
520 	codec->devinfo = devinfo;
521 	codec->flags = 0;
522 	return codec;
523 }
524 
525 void
526 ac97_destroy(struct ac97_info *codec)
527 {
528 	snd_mtxlock(codec->lock);
529 	if (codec->methods != NULL)
530 		kobj_delete(codec->methods, M_AC97);
531 	snd_mtxfree(codec->lock);
532 	free(codec, M_AC97);
533 }
534 
535 void
536 ac97_setflags(struct ac97_info *codec, u_int32_t val)
537 {
538 	codec->flags = val;
539 }
540 
541 u_int32_t
542 ac97_getflags(struct ac97_info *codec)
543 {
544 	return codec->flags;
545 }
546 
547 /* -------------------------------------------------------------------- */
548 
549 static int
550 ac97mix_init(struct snd_mixer *m)
551 {
552 	struct ac97_info *codec = mix_getdevinfo(m);
553 	u_int32_t i, mask;
554 
555 	if (codec == NULL)
556 		return -1;
557 
558 	if (ac97_initmixer(codec))
559 		return -1;
560 
561 	mask = 0;
562 	for (i = 0; i < 32; i++)
563 		mask |= codec->mix[i].enable? 1 << i : 0;
564 	mix_setdevs(m, mask);
565 
566 	mask = 0;
567 	for (i = 0; i < 32; i++)
568 		mask |= codec->mix[i].recidx? 1 << i : 0;
569 	mix_setrecdevs(m, mask);
570 	return 0;
571 }
572 
573 static int
574 ac97mix_uninit(struct snd_mixer *m)
575 {
576 	struct ac97_info *codec = mix_getdevinfo(m);
577 
578 	if (codec == NULL)
579 		return -1;
580 	/*
581 	if (ac97_uninitmixer(codec))
582 		return -1;
583 	*/
584 	ac97_destroy(codec);
585 	return 0;
586 }
587 
588 static int
589 ac97mix_reinit(struct snd_mixer *m)
590 {
591 	struct ac97_info *codec = mix_getdevinfo(m);
592 
593 	if (codec == NULL)
594 		return -1;
595 	return ac97_reinitmixer(codec);
596 }
597 
598 static int
599 ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
600 {
601 	struct ac97_info *codec = mix_getdevinfo(m);
602 
603 	if (codec == NULL)
604 		return -1;
605 	return ac97_setmixer(codec, dev, left, right);
606 }
607 
608 static int
609 ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
610 {
611 	int i;
612 	struct ac97_info *codec = mix_getdevinfo(m);
613 
614 	if (codec == NULL)
615 		return -1;
616 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
617 		if ((src & (1 << i)) != 0)
618 			break;
619 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
620 }
621 
622 static kobj_method_t ac97mixer_methods[] = {
623     	KOBJMETHOD(mixer_init,		ac97mix_init),
624     	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
625     	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
626     	KOBJMETHOD(mixer_set,		ac97mix_set),
627     	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
628 	{ 0, 0 }
629 };
630 MIXER_DECLARE(ac97mixer);
631 
632 /* -------------------------------------------------------------------- */
633 
634 kobj_class_t
635 ac97_getmixerclass(void)
636 {
637 	return &ac97mixer_class;
638 }
639 
640 
641