xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision 71fe318b852b8dfb3e799cb12ef184750f7f8eac)
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_AUXOUT, 	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 void
372 ac97_fix_auxout(struct ac97_info *codec)
373 {
374 	/* Determine what AUXOUT really means, it can be:
375 	 *
376 	 * 1. Headphone out.
377 	 * 2. 4-Channel Out
378 	 * 3. True line level out (effectively master volume).
379 	 *
380 	 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
381 	 */
382 	if (codec->caps & AC97_CAP_HEADPHONE) {
383 		/* XXX We should probably check the AUX_OUT initial value.
384 		 * Leave AC97_MIX_AUXOUT - SOUND_MIXER_MONITOR relationship */
385 		return;
386 	} else if (codec->extcaps & AC97_EXTCAP_SDAC &&
387 		   rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
388 		/* 4-Channel Out, add an additional gain setting. */
389 		codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR];
390 	} else {
391 		/* Master volume is/maybe fixed in h/w, not sufficiently
392 		 * clear in spec to blat SOUND_MIXER_MASTER. */
393 		codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR];
394 	}
395 	/* Blat monitor, inappropriate label if we get here */
396 	bzero(&codec->mix[SOUND_MIXER_MONITOR],
397 	      sizeof(codec->mix[SOUND_MIXER_MONITOR]));
398 }
399 
400 static unsigned
401 ac97_initmixer(struct ac97_info *codec)
402 {
403 	unsigned i, j, k, old;
404 	u_int32_t id;
405 
406 	snd_mtxlock(codec->lock);
407 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
408 	if (codec->count == 0) {
409 		device_printf(codec->dev, "ac97 codec init failed\n");
410 		snd_mtxunlock(codec->lock);
411 		return ENODEV;
412 	}
413 
414 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
415 	ac97_reset(codec);
416 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
417 
418 	i = rdcd(codec, AC97_REG_RESET);
419 	codec->caps = i & 0x03ff;
420 	codec->se =  (i & 0x7c00) >> 10;
421 
422 	id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2);
423 	codec->rev = id & 0x000000ff;
424 	if (id == 0 || id == 0xffffffff) {
425 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
426 		snd_mtxunlock(codec->lock);
427 		return ENODEV;
428 	}
429 
430 	codec->noext = 0;
431 	codec->id = NULL;
432 	for (i = 0; ac97codecid[i].id; i++) {
433 		if (ac97codecid[i].id == id) {
434 			codec->id = ac97codecid[i].name;
435 			codec->noext = ac97codecid[i].noext;
436 		}
437 	}
438 
439 	codec->extcaps = 0;
440 	codec->extid = 0;
441 	codec->extstat = 0;
442 	if (!codec->noext) {
443 		i = rdcd(codec, AC97_REGEXT_ID);
444 		if (i != 0xffff) {
445 			codec->extcaps = i & 0x3fff;
446 			codec->extid =  (i & 0xc000) >> 14;
447 			codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
448 		}
449 	}
450 
451 	for (i = 0; i < 32; i++) {
452 		codec->mix[i] = ac97mixtable_default[i];
453 	}
454 	ac97_fix_auxout(codec);
455 
456 	for (i = 0; i < 32; i++) {
457 		k = codec->noext? codec->mix[i].enable : 1;
458 		if (k && (codec->mix[i].reg > 0)) {
459 			old = rdcd(codec, codec->mix[i].reg);
460 			wrcd(codec, codec->mix[i].reg, 0x3f);
461 			j = rdcd(codec, codec->mix[i].reg);
462 			wrcd(codec, codec->mix[i].reg, old);
463 			codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
464 			for (k = 1; j & (1 << k); k++);
465 			codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
466 		}
467 		/* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
468 	}
469 
470 	if (bootverbose) {
471 		device_printf(codec->dev, "ac97 codec id 0x%08x", id);
472 		if (codec->id)
473 			printf(" (%s)", codec->id);
474 		printf("\n");
475 		device_printf(codec->dev, "ac97 codec features ");
476 		for (i = j = 0; i < 10; i++)
477 			if (codec->caps & (1 << i))
478 				printf("%s%s", j++? ", " : "", ac97feature[i]);
479 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
480 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
481 
482 		if (codec->extcaps != 0 || codec->extid) {
483 			device_printf(codec->dev, "ac97 %s codec",
484 				      codec->extid? "secondary" : "primary");
485 			if (codec->extcaps)
486 				printf(" extended features ");
487 			for (i = j = 0; i < 14; i++)
488 				if (codec->extcaps & (1 << i))
489 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
490 			printf("\n");
491 		}
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 static unsigned
501 ac97_reinitmixer(struct ac97_info *codec)
502 {
503 	snd_mtxlock(codec->lock);
504 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
505 	if (codec->count == 0) {
506 		device_printf(codec->dev, "ac97 codec init failed\n");
507 		snd_mtxunlock(codec->lock);
508 		return ENODEV;
509 	}
510 
511 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
512 	ac97_reset(codec);
513 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
514 
515 	if (!codec->noext) {
516 		wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
517 		if ((rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
518 		    != codec->extstat)
519 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
520 				      codec->extstat,
521 				      rdcd(codec, AC97_REGEXT_STAT) &
522 					AC97_EXTCAPS);
523 	}
524 
525 	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
526 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
527 	snd_mtxunlock(codec->lock);
528 	return 0;
529 }
530 
531 struct ac97_info *
532 ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
533 {
534 	struct ac97_info *codec;
535 
536 	codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
537 	if (codec == NULL)
538 		return NULL;
539 
540 	snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
541 	codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
542 	codec->methods = kobj_create(cls, M_AC97, M_WAITOK);
543 	if (codec->methods == NULL) {
544 		snd_mtxlock(codec->lock);
545 		snd_mtxfree(codec->lock);
546 		free(codec, M_AC97);
547 		return NULL;
548 	}
549 
550 	codec->dev = dev;
551 	codec->devinfo = devinfo;
552 	codec->flags = 0;
553 	return codec;
554 }
555 
556 void
557 ac97_destroy(struct ac97_info *codec)
558 {
559 	snd_mtxlock(codec->lock);
560 	if (codec->methods != NULL)
561 		kobj_delete(codec->methods, M_AC97);
562 	snd_mtxfree(codec->lock);
563 	free(codec, M_AC97);
564 }
565 
566 void
567 ac97_setflags(struct ac97_info *codec, u_int32_t val)
568 {
569 	codec->flags = val;
570 }
571 
572 u_int32_t
573 ac97_getflags(struct ac97_info *codec)
574 {
575 	return codec->flags;
576 }
577 
578 /* -------------------------------------------------------------------- */
579 
580 static int
581 ac97mix_init(struct snd_mixer *m)
582 {
583 	struct ac97_info *codec = mix_getdevinfo(m);
584 	u_int32_t i, mask;
585 
586 	if (codec == NULL)
587 		return -1;
588 
589 	if (ac97_initmixer(codec))
590 		return -1;
591 
592 	mask = 0;
593 	for (i = 0; i < 32; i++)
594 		mask |= codec->mix[i].enable? 1 << i : 0;
595 	mix_setdevs(m, mask);
596 
597 	mask = 0;
598 	for (i = 0; i < 32; i++)
599 		mask |= codec->mix[i].recidx? 1 << i : 0;
600 	mix_setrecdevs(m, mask);
601 	return 0;
602 }
603 
604 static int
605 ac97mix_uninit(struct snd_mixer *m)
606 {
607 	struct ac97_info *codec = mix_getdevinfo(m);
608 
609 	if (codec == NULL)
610 		return -1;
611 	/*
612 	if (ac97_uninitmixer(codec))
613 		return -1;
614 	*/
615 	ac97_destroy(codec);
616 	return 0;
617 }
618 
619 static int
620 ac97mix_reinit(struct snd_mixer *m)
621 {
622 	struct ac97_info *codec = mix_getdevinfo(m);
623 
624 	if (codec == NULL)
625 		return -1;
626 	return ac97_reinitmixer(codec);
627 }
628 
629 static int
630 ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
631 {
632 	struct ac97_info *codec = mix_getdevinfo(m);
633 
634 	if (codec == NULL)
635 		return -1;
636 	return ac97_setmixer(codec, dev, left, right);
637 }
638 
639 static int
640 ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
641 {
642 	int i;
643 	struct ac97_info *codec = mix_getdevinfo(m);
644 
645 	if (codec == NULL)
646 		return -1;
647 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
648 		if ((src & (1 << i)) != 0)
649 			break;
650 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
651 }
652 
653 static kobj_method_t ac97mixer_methods[] = {
654     	KOBJMETHOD(mixer_init,		ac97mix_init),
655     	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
656     	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
657     	KOBJMETHOD(mixer_set,		ac97mix_set),
658     	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
659 	{ 0, 0 }
660 };
661 MIXER_DECLARE(ac97mixer);
662 
663 /* -------------------------------------------------------------------- */
664 
665 kobj_class_t
666 ac97_getmixerclass(void)
667 {
668 	return &ac97mixer_class;
669 }
670 
671 
672