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