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