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