xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision eacee0ff7ec955b32e09515246bd97b6edcd2b0f)
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 int
204 ac97_setrate(struct ac97_info *codec, int which, int rate)
205 {
206 	u_int16_t v;
207 
208 	switch(which) {
209 	case AC97_REGEXT_FDACRATE:
210 	case AC97_REGEXT_SDACRATE:
211 	case AC97_REGEXT_LDACRATE:
212 	case AC97_REGEXT_LADCRATE:
213 	case AC97_REGEXT_MADCRATE:
214 		break;
215 
216 	default:
217 		return -1;
218 	}
219 
220 	snd_mtxlock(codec->lock);
221 	if (rate != 0) {
222 		v = rate;
223 		if (codec->extstat & AC97_EXTCAP_DRA)
224 			v >>= 1;
225 		wrcd(codec, which, v);
226 	}
227 	v = rdcd(codec, which);
228 	if (codec->extstat & AC97_EXTCAP_DRA)
229 		v <<= 1;
230 	snd_mtxunlock(codec->lock);
231 	return v;
232 }
233 
234 int
235 ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
236 {
237 	mode &= AC97_EXTCAPS;
238 	if ((mode & ~codec->extcaps) != 0) {
239 		device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
240 			      mode);
241 		return -1;
242 	}
243 	snd_mtxlock(codec->lock);
244 	wrcd(codec, AC97_REGEXT_STAT, mode);
245 	codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
246 	snd_mtxunlock(codec->lock);
247 	return (mode == codec->extstat)? 0 : -1;
248 }
249 
250 u_int16_t
251 ac97_getextmode(struct ac97_info *codec)
252 {
253 	return codec->extstat;
254 }
255 
256 u_int16_t
257 ac97_getextcaps(struct ac97_info *codec)
258 {
259 	return codec->extcaps;
260 }
261 
262 u_int16_t
263 ac97_getcaps(struct ac97_info *codec)
264 {
265 	return codec->caps;
266 }
267 
268 static int
269 ac97_setrecsrc(struct ac97_info *codec, int channel)
270 {
271 	struct ac97mixtable_entry *e = &codec->mix[channel];
272 
273 	if (e->recidx > 0) {
274 		int val = e->recidx - 1;
275 		val |= val << 8;
276 		snd_mtxlock(codec->lock);
277 		wrcd(codec, AC97_REG_RECSEL, val);
278 		snd_mtxunlock(codec->lock);
279 		return 0;
280 	} else
281 		return -1;
282 }
283 
284 static int
285 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
286 {
287 	struct ac97mixtable_entry *e = &codec->mix[channel];
288 
289 	if (e->reg && e->enable && e->bits) {
290 		int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
291 
292 		if (!e->stereo)
293 			right = left;
294 		if (e->reg > 0) {
295 			left = 100 - left;
296 			right = 100 - right;
297 		}
298 
299 		max = (1 << e->bits) - 1;
300 		left = (left * max) / 100;
301 		right = (right * max) / 100;
302 
303 		val = (left << 8) | right;
304 
305 		left = (left * 100) / max;
306 		right = (right * 100) / max;
307 
308 		if (e->reg > 0) {
309 			left = 100 - left;
310 			right = 100 - right;
311 		}
312 
313 		if (!e->stereo) {
314 			val &= max;
315 			val <<= e->ofs;
316 			if (e->mask) {
317 				int cur = rdcd(codec, e->reg);
318 				val |= cur & ~(max << e->ofs);
319 			}
320 		}
321 		if (left == 0 && right == 0 && e->mute == 1)
322 			val = AC97_MUTE;
323 		snd_mtxlock(codec->lock);
324 		wrcd(codec, reg, val);
325 		snd_mtxunlock(codec->lock);
326 		return left | (right << 8);
327 	} else {
328 		/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
329 		return -1;
330 	}
331 }
332 
333 #if 0
334 static int
335 ac97_getmixer(struct ac97_info *codec, int channel)
336 {
337 	struct ac97mixtable_entry *e = &codec->mix[channel];
338 	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
339 		int max, val, volume;
340 
341 		max = (1 << e->bits) - 1;
342 		val = rdcd(code, e->reg);
343 		if (val == AC97_MUTE && e->mute == 1)
344 			volume = 0;
345 		else {
346 			if (e->stereo == 0) val >>= e->ofs;
347 			val &= max;
348 			volume = (val * 100) / max;
349 			if (e->reg > 0) volume = 100 - volume;
350 		}
351 		return volume;
352 	} else
353 		return -1;
354 }
355 #endif
356 
357 static unsigned
358 ac97_initmixer(struct ac97_info *codec)
359 {
360 	unsigned i, j, k, old;
361 	u_int32_t id;
362 
363 	snd_mtxlock(codec->lock);
364 	for (i = 0; i < 32; i++)
365 		codec->mix[i] = ac97mixtable_default[i];
366 
367 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
368 	if (codec->count == 0) {
369 		device_printf(codec->dev, "ac97 codec init failed\n");
370 		snd_mtxunlock(codec->lock);
371 		return ENODEV;
372 	}
373 
374 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
375 	wrcd(codec, AC97_REG_RESET, 0);
376 	DELAY(100000);
377 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
378 
379 	i = rdcd(codec, AC97_REG_RESET);
380 	codec->caps = i & 0x03ff;
381 	codec->se =  (i & 0x7c00) >> 10;
382 
383 	id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2);
384 	codec->rev = id & 0x000000ff;
385 	if (id == 0 || id == 0xffffffff) {
386 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
387 		snd_mtxunlock(codec->lock);
388 		return ENODEV;
389 	}
390 
391 	codec->noext = 0;
392 	codec->id = NULL;
393 	for (i = 0; ac97codecid[i].id; i++) {
394 		if (ac97codecid[i].id == id) {
395 			codec->id = ac97codecid[i].name;
396 			codec->noext = ac97codecid[i].noext;
397 		}
398 	}
399 
400 	codec->extcaps = 0;
401 	codec->extid = 0;
402 	codec->extstat = 0;
403 	if (!codec->noext) {
404 		i = rdcd(codec, AC97_REGEXT_ID);
405 		if (i != 0xffff) {
406 			codec->extcaps = i & 0x3fff;
407 			codec->extid =  (i & 0xc000) >> 14;
408 			codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
409 		}
410 	}
411 
412 	for (i = 0; i < 32; i++) {
413 		k = codec->noext? codec->mix[i].enable : 1;
414 		if (k && (codec->mix[i].reg > 0)) {
415 			old = rdcd(codec, codec->mix[i].reg);
416 			wrcd(codec, codec->mix[i].reg, 0x3f);
417 			j = rdcd(codec, codec->mix[i].reg);
418 			wrcd(codec, codec->mix[i].reg, old);
419 			codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
420 			for (k = 1; j & (1 << k); k++);
421 			codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
422 		}
423 		/* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
424 	}
425 
426 	if (bootverbose) {
427 		device_printf(codec->dev, "ac97 codec id 0x%08x", id);
428 		if (codec->id)
429 			printf(" (%s)", codec->id);
430 		printf("\n");
431 		device_printf(codec->dev, "ac97 codec features ");
432 		for (i = j = 0; i < 10; i++)
433 			if (codec->caps & (1 << i))
434 				printf("%s%s", j++? ", " : "", ac97feature[i]);
435 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
436 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
437 
438 		if (codec->extcaps != 0 || codec->extid) {
439 			device_printf(codec->dev, "ac97 %s codec",
440 				      codec->extid? "secondary" : "primary");
441 			if (codec->extcaps)
442 				printf(" extended features ");
443 			for (i = j = 0; i < 14; i++)
444 				if (codec->extcaps & (1 << i))
445 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
446 			printf("\n");
447 		}
448 	}
449 
450 	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
451 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
452 	snd_mtxunlock(codec->lock);
453 	return 0;
454 }
455 
456 static unsigned
457 ac97_reinitmixer(struct ac97_info *codec)
458 {
459 	unsigned i;
460 
461 	snd_mtxlock(codec->lock);
462 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
463 	if (codec->count == 0) {
464 		device_printf(codec->dev, "ac97 codec init failed\n");
465 		snd_mtxunlock(codec->lock);
466 		return ENODEV;
467 	}
468 
469 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
470 	wrcd(codec, AC97_REG_RESET, 0);
471 	DELAY(100000);
472 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
473 	i = rdcd(codec, AC97_REG_RESET);
474 
475 	if (!codec->noext) {
476 		wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
477 		if ((rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
478 		    != codec->extstat)
479 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
480 				      codec->extstat,
481 				      rdcd(codec, AC97_REGEXT_STAT) &
482 					AC97_EXTCAPS);
483 	}
484 
485 	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
486 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
487 	snd_mtxunlock(codec->lock);
488 	return 0;
489 }
490 
491 struct ac97_info *
492 ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
493 {
494 	struct ac97_info *codec;
495 
496 	codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
497 	if (codec == NULL)
498 		return NULL;
499 
500 	snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
501 	codec->lock = snd_mtxcreate(codec->name);
502 	codec->methods = kobj_create(cls, M_AC97, M_WAITOK);
503 	if (codec->methods == NULL) {
504 		snd_mtxlock(codec->lock);
505 		snd_mtxfree(codec->lock);
506 		free(codec, M_AC97);
507 		return NULL;
508 	}
509 
510 	codec->dev = dev;
511 	codec->devinfo = devinfo;
512 	codec->flags = 0;
513 	return codec;
514 }
515 
516 void
517 ac97_destroy(struct ac97_info *codec)
518 {
519 	snd_mtxlock(codec->lock);
520 	if (codec->methods != NULL)
521 		kobj_delete(codec->methods, M_AC97);
522 	snd_mtxfree(codec->lock);
523 	free(codec, M_AC97);
524 }
525 
526 void
527 ac97_setflags(struct ac97_info *codec, u_int32_t val)
528 {
529 	codec->flags = val;
530 }
531 
532 u_int32_t
533 ac97_getflags(struct ac97_info *codec)
534 {
535 	return codec->flags;
536 }
537 
538 /* -------------------------------------------------------------------- */
539 
540 static int
541 ac97mix_init(struct snd_mixer *m)
542 {
543 	struct ac97_info *codec = mix_getdevinfo(m);
544 	u_int32_t i, mask;
545 
546 	if (codec == NULL)
547 		return -1;
548 
549 	if (ac97_initmixer(codec))
550 		return -1;
551 
552 	mask = 0;
553 	for (i = 0; i < 32; i++)
554 		mask |= codec->mix[i].enable? 1 << i : 0;
555 	mix_setdevs(m, mask);
556 
557 	mask = 0;
558 	for (i = 0; i < 32; i++)
559 		mask |= codec->mix[i].recidx? 1 << i : 0;
560 	mix_setrecdevs(m, mask);
561 	return 0;
562 }
563 
564 static int
565 ac97mix_uninit(struct snd_mixer *m)
566 {
567 	struct ac97_info *codec = mix_getdevinfo(m);
568 
569 	if (codec == NULL)
570 		return -1;
571 	/*
572 	if (ac97_uninitmixer(codec))
573 		return -1;
574 	*/
575 	ac97_destroy(codec);
576 	return 0;
577 }
578 
579 static int
580 ac97mix_reinit(struct snd_mixer *m)
581 {
582 	struct ac97_info *codec = mix_getdevinfo(m);
583 
584 	if (codec == NULL)
585 		return -1;
586 	return ac97_reinitmixer(codec);
587 }
588 
589 static int
590 ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
591 {
592 	struct ac97_info *codec = mix_getdevinfo(m);
593 
594 	if (codec == NULL)
595 		return -1;
596 	return ac97_setmixer(codec, dev, left, right);
597 }
598 
599 static int
600 ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
601 {
602 	int i;
603 	struct ac97_info *codec = mix_getdevinfo(m);
604 
605 	if (codec == NULL)
606 		return -1;
607 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
608 		if ((src & (1 << i)) != 0)
609 			break;
610 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
611 }
612 
613 static kobj_method_t ac97mixer_methods[] = {
614     	KOBJMETHOD(mixer_init,		ac97mix_init),
615     	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
616     	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
617     	KOBJMETHOD(mixer_set,		ac97mix_set),
618     	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
619 	{ 0, 0 }
620 };
621 MIXER_DECLARE(ac97mixer);
622 
623 /* -------------------------------------------------------------------- */
624 
625 kobj_class_t
626 ac97_getmixerclass(void)
627 {
628 	return &ac97mixer_class;
629 }
630 
631 
632