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