xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
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$
27  */
28 
29 #include <dev/sound/pcm/sound.h>
30 #include <dev/sound/pcm/ac97.h>
31 
32 struct ac97mixtable_entry {
33 	int		reg:8;
34 	unsigned	bits:4;
35 	unsigned	ofs:4;
36 	unsigned	stereo:1;
37 	unsigned	mute:1;
38 	unsigned	recidx:4;
39 	unsigned        mask:1;
40 	unsigned	enable:1;
41 };
42 
43 struct ac97_info {
44 	device_t dev;
45 	ac97_init *init;
46 	ac97_read *read;
47 	ac97_write *write;
48 	void *devinfo;
49 	char *name;
50 	char rev;
51 	unsigned count, caps, se, extcaps, extid, extstat, noext:1;
52 	struct ac97mixtable_entry mix[32];
53 };
54 
55 struct ac97_codecid {
56 	u_int32_t id, noext:1;
57 	char *name;
58 };
59 
60 static const struct ac97mixtable_entry ac97mixtable_default[32] = {
61 	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0, 1 },
62 	[SOUND_MIXER_MONITOR]	= { AC97_MIX_PHONES, 	5, 0, 1, 1, 0, 0, 0 },
63 	[SOUND_MIXER_PHONEOUT]	= { AC97_MIX_MONO, 	5, 0, 0, 1, 7, 0, 0 },
64 	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1, 0 },
65 	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1, 0 },
66 	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0, 1 },
67 	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0, 0 },
68 	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0, 1 },
69 	[SOUND_MIXER_PHONEIN]	= { AC97_MIX_PHONE, 	5, 0, 0, 1, 8, 0, 0 },
70 	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 0, 1 },
71 	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
72 	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
73 	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
74 	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
75 };
76 
77 static struct ac97_codecid ac97codecid[] = {
78 	{ 0x414b4d00, 1, "Asahi Kasei AK4540 rev 0" },
79 	{ 0x414b4d01, 1, "Asahi Kasei AK4540 rev 1" },
80 	{ 0x43525900, 0, "Cirrus Logic CS4297" 	},
81 	{ 0x83847600, 0, "SigmaTel STAC????" 	},
82 	{ 0x83847604, 0, "SigmaTel STAC9701/3/4/5" },
83 	{ 0x83847605, 0, "SigmaTel STAC9704" 	},
84 	{ 0x83847608, 0, "SigmaTel STAC9708" 	},
85 	{ 0x83847609, 0, "SigmaTel STAC9721" 	},
86 	{ 0, 	      0, NULL			}
87 };
88 
89 static char *ac97enhancement[] = {
90 	"no 3D Stereo Enhancement",
91 	"Analog Devices Phat Stereo",
92 	"Creative Stereo Enhancement",
93 	"National Semi 3D Stereo Enhancement",
94 	"Yamaha Ymersion",
95 	"BBE 3D Stereo Enhancement",
96 	"Crystal Semi 3D Stereo Enhancement",
97 	"Qsound QXpander",
98 	"Spatializer 3D Stereo Enhancement",
99 	"SRS 3D Stereo Enhancement",
100 	"Platform Tech 3D Stereo Enhancement",
101 	"AKM 3D Audio",
102 	"Aureal Stereo Enhancement",
103 	"Aztech 3D Enhancement",
104 	"Binaura 3D Audio Enhancement",
105 	"ESS Technology Stereo Enhancement",
106 	"Harman International VMAx",
107 	"Nvidea 3D Stereo Enhancement",
108 	"Philips Incredible Sound",
109 	"Texas Instruments 3D Stereo Enhancement",
110 	"VLSI Technology 3D Stereo Enhancement",
111 	"TriTech 3D Stereo Enhancement",
112 	"Realtek 3D Stereo Enhancement",
113 	"Samsung 3D Stereo Enhancement",
114 	"Wolfson Microelectronics 3D Enhancement",
115 	"Delta Integration 3D Enhancement",
116 	"SigmaTel 3D Enhancement",
117 	"Reserved 27",
118 	"Rockwell 3D Stereo Enhancement",
119 	"Reserved 29",
120 	"Reserved 30",
121 	"Reserved 31"
122 };
123 
124 static char *ac97feature[] = {
125 	"mic channel",
126 	"reserved",
127 	"tone",
128 	"simulated stereo",
129 	"headphone",
130 	"bass boost",
131 	"18 bit DAC",
132 	"20 bit DAC",
133 	"18 bit ADC",
134 	"20 bit ADC"
135 };
136 
137 static char *ac97extfeature[] = {
138 	"variable rate PCM",
139 	"double rate PCM",
140 	"reserved 1",
141 	"variable rate mic",
142 	"reserved 2",
143 	"reserved 3",
144 	"center DAC",
145 	"surround DAC",
146 	"LFE DAC",
147 	"AMAP",
148 	"reserved 4",
149 	"reserved 5",
150 	"reserved 6",
151 	"reserved 7",
152 };
153 
154 static u_int16_t
155 rdcd(struct ac97_info *codec, int reg)
156 {
157 	return codec->read(codec->devinfo, reg);
158 }
159 
160 static void
161 wrcd(struct ac97_info *codec, int reg, u_int16_t val)
162 {
163 	codec->write(codec->devinfo, reg, val);
164 }
165 
166 int
167 ac97_setrate(struct ac97_info *codec, int which, int rate)
168 {
169 	u_int16_t v;
170 
171 	switch(which) {
172 	case AC97_REGEXT_FDACRATE:
173 	case AC97_REGEXT_SDACRATE:
174 	case AC97_REGEXT_LDACRATE:
175 	case AC97_REGEXT_LADCRATE:
176 	case AC97_REGEXT_MADCRATE:
177 		break;
178 
179 	default:
180 		return -1;
181 	}
182 
183 	if (rate != 0) {
184 		v = rate;
185 		if (codec->extstat & AC97_EXTCAP_DRA)
186 			v >>= 1;
187 		wrcd(codec, which, v);
188 	}
189 	v = rdcd(codec, which);
190 	if (codec->extstat & AC97_EXTCAP_DRA)
191 		v <<= 1;
192 	return v;
193 }
194 
195 int
196 ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
197 {
198 	mode &= AC97_EXTCAPS;
199 	if ((mode & ~codec->extcaps) != 0)
200 		return -1;
201 	wrcd(codec, AC97_REGEXT_STAT, mode);
202 	codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
203 	return (mode == codec->extstat)? 0 : -1;
204 }
205 
206 u_int16_t
207 ac97_getextmode(struct ac97_info *codec)
208 {
209 	return codec->extstat;
210 }
211 
212 u_int16_t
213 ac97_getextcaps(struct ac97_info *codec)
214 {
215 	return codec->extcaps;
216 }
217 
218 static int
219 ac97_setrecsrc(struct ac97_info *codec, int channel)
220 {
221 	struct ac97mixtable_entry *e = &codec->mix[channel];
222 
223 	if (e->recidx > 0) {
224 		int val = e->recidx - 1;
225 		val |= val << 8;
226 		wrcd(codec, AC97_REG_RECSEL, val);
227 		return 0;
228 	} else
229 		return -1;
230 }
231 
232 static int
233 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
234 {
235 	struct ac97mixtable_entry *e = &codec->mix[channel];
236 
237 	if (e->reg && e->enable && e->bits) {
238 		int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
239 
240 		if (!e->stereo)
241 			right = left;
242 		if (e->reg > 0) {
243 			left = 100 - left;
244 			right = 100 - right;
245 		}
246 
247 		max = (1 << e->bits) - 1;
248 		left = (left * max) / 100;
249 		right = (right * max) / 100;
250 
251 		val = (left << 8) | right;
252 
253 		left = (left * 100) / max;
254 		right = (right * 100) / max;
255 
256 		if (e->reg > 0) {
257 			left = 100 - left;
258 			right = 100 - right;
259 		}
260 
261 		if (!e->stereo) {
262 			val &= max;
263 			val <<= e->ofs;
264 			if (e->mask) {
265 				int cur = rdcd(codec, e->reg);
266 				val |= cur & ~(max << e->ofs);
267 			}
268 		}
269 		if (left == 0 && right == 0 && e->mute == 1)
270 			val = AC97_MUTE;
271 		wrcd(codec, reg, val);
272 		return left | (right << 8);
273 	} else {
274 		/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
275 		return -1;
276 	}
277 }
278 
279 #if 0
280 static int
281 ac97_getmixer(struct ac97_info *codec, int channel)
282 {
283 	struct ac97mixtable_entry *e = &codec->mix[channel];
284 	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
285 		int max, val, volume;
286 
287 		max = (1 << e->bits) - 1;
288 		val = rdcd(code, e->reg);
289 		if (val == AC97_MUTE && e->mute == 1)
290 			volume = 0;
291 		else {
292 			if (e->stereo == 0) val >>= e->ofs;
293 			val &= max;
294 			volume = (val * 100) / max;
295 			if (e->reg > 0) volume = 100 - volume;
296 		}
297 		return volume;
298 	} else
299 		return -1;
300 }
301 #endif
302 
303 static unsigned
304 ac97_initmixer(struct ac97_info *codec)
305 {
306 	unsigned i, j, k, old;
307 	u_int32_t id;
308 
309 	for (i = 0; i < 32; i++)
310 		codec->mix[i] = ac97mixtable_default[i];
311 
312 	if (codec->init) {
313 		codec->count = codec->init(codec->devinfo);
314 		if (codec->count == 0) {
315 			device_printf(codec->dev, "ac97 codec init failed\n");
316 			return ENODEV;
317 		}
318 	} else
319 		codec->count = 1;
320 
321 	wrcd(codec, AC97_REG_POWER, 0);
322 	wrcd(codec, AC97_REG_RESET, 0);
323 	DELAY(100000);
324 
325 	i = rdcd(codec, AC97_REG_RESET);
326 	codec->caps = i & 0x03ff;
327 	codec->se =  (i & 0x7c00) >> 10;
328 
329 	id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2);
330 	codec->rev = id & 0x000000ff;
331 	if (id == 0 || id == 0xffffffff) {
332 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
333 		return ENODEV;
334 	}
335 
336 	codec->noext = 0;
337 	codec->name = NULL;
338 	for (i = 0; ac97codecid[i].id; i++) {
339 		if (ac97codecid[i].id == id) {
340 			codec->name = ac97codecid[i].name;
341 			codec->noext = ac97codecid[i].noext;
342 		}
343 	}
344 
345 	if (codec->noext) {
346 		codec->extcaps = 0;
347 		codec->extid = 0;
348 		codec->extstat = 0;
349 	} else {
350 		i = rdcd(codec, AC97_REGEXT_ID);
351 		codec->extcaps = i & 0x3fff;
352 		codec->extid =  (i & 0xc000) >> 14;
353 		codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
354 	}
355 
356 	for (i = 0; i < 32; i++) {
357 		k = codec->noext? codec->mix[i].enable : 1;
358 		if (k && (codec->mix[i].reg > 0)) {
359 			old = rdcd(codec, codec->mix[i].reg);
360 			wrcd(codec, codec->mix[i].reg, 0x3f);
361 			j = rdcd(codec, codec->mix[i].reg);
362 			wrcd(codec, codec->mix[i].reg, old);
363 			codec->mix[i].enable = j? 1 : 0;
364 			for (k = 1; j & (1 << k); k++);
365 			codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
366 		}
367 		/* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
368 	}
369 
370 	if (bootverbose) {
371 		device_printf(codec->dev, "ac97 codec id 0x%08x", id);
372 		if (codec->name)
373 			printf(" (%s)", codec->name);
374 		printf("\n");
375 		device_printf(codec->dev, "ac97 codec features ");
376 		for (i = j = 0; i < 10; i++)
377 			if (codec->caps & (1 << i))
378 				printf("%s%s", j++? ", " : "", ac97feature[i]);
379 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
380 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
381 
382 		if (codec->extcaps != 0 || codec->extid) {
383 			device_printf(codec->dev, "ac97 %s codec",
384 				      codec->extid? "secondary" : "primary");
385 			if (codec->extcaps)
386 				printf(" extended features ");
387 			for (i = j = 0; i < 14; i++)
388 				if (codec->extcaps & (1 << i))
389 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
390 			printf("\n");
391 		}
392 	}
393 
394 	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
395 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
396 	return 0;
397 }
398 
399 static unsigned
400 ac97_reinitmixer(struct ac97_info *codec)
401 {
402 	unsigned i;
403 
404 	if (codec->init) {
405 		codec->count = codec->init(codec->devinfo);
406 		if (codec->count == 0) {
407 			device_printf(codec->dev, "ac97 codec init failed\n");
408 			return ENODEV;
409 		}
410 	} else
411 		codec->count = 1;
412 
413 	wrcd(codec, AC97_REG_POWER, 0);
414 	wrcd(codec, AC97_REG_RESET, 0);
415 	DELAY(100000);
416 	i = rdcd(codec, AC97_REG_RESET);
417 
418 	if (!codec->noext) {
419 		wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
420 		if (rdcd(codec, AC97_REGEXT_STAT) != codec->extstat)
421 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
422 				      codec->extstat, rdcd(codec, AC97_REGEXT_STAT));
423 	}
424 
425 	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
426 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
427 	return 0;
428 }
429 
430 struct ac97_info *
431 ac97_create(device_t dev, void *devinfo, ac97_init *init, ac97_read *rd, ac97_write *wr)
432 {
433 	struct ac97_info *codec;
434 
435 	codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT);
436 	if (codec != NULL) {
437 		codec->dev = dev;
438 		codec->init = init;
439 		codec->read = rd;
440 		codec->write = wr;
441 		codec->devinfo = devinfo;
442 	}
443 	return codec;
444 }
445 
446 void
447 ac97_destroy(struct ac97_info *codec)
448 {
449 	free(codec, M_DEVBUF);
450 }
451 
452 static int
453 ac97mix_init(snd_mixer *m)
454 {
455 	struct ac97_info *codec = mix_getdevinfo(m);
456 	u_int32_t i, mask;
457 
458 	if (codec == NULL)
459 		return -1;
460 
461 	if (ac97_initmixer(codec))
462 		return -1;
463 
464 	mask = 0;
465 	for (i = 0; i < 32; i++)
466 		mask |= codec->mix[i].enable? 1 << i : 0;
467 	mix_setdevs(m, mask);
468 
469 	mask = 0;
470 	for (i = 0; i < 32; i++)
471 		mask |= codec->mix[i].recidx? 1 << i : 0;
472 	mix_setrecdevs(m, mask);
473 	return 0;
474 }
475 
476 static int
477 ac97mix_uninit(snd_mixer *m)
478 {
479 	struct ac97_info *codec = mix_getdevinfo(m);
480 
481 	if (codec == NULL)
482 		return -1;
483 	/*
484 	if (ac97_uninitmixer(codec))
485 		return -1;
486 	*/
487 	ac97_destroy(codec);
488 	return 0;
489 }
490 
491 static int
492 ac97mix_reinit(snd_mixer *m)
493 {
494 	struct ac97_info *codec = mix_getdevinfo(m);
495 
496 	if (codec == NULL)
497 		return -1;
498 	return ac97_reinitmixer(codec);
499 }
500 
501 static int
502 ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
503 {
504 	struct ac97_info *codec = mix_getdevinfo(m);
505 
506 	if (codec == NULL)
507 		return -1;
508 	return ac97_setmixer(codec, dev, left, right);
509 }
510 
511 static int
512 ac97mix_setrecsrc(snd_mixer *m, u_int32_t src)
513 {
514 	int i;
515 	struct ac97_info *codec = mix_getdevinfo(m);
516 
517 	if (codec == NULL)
518 		return -1;
519 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
520 		if ((src & (1 << i)) != 0)
521 			break;
522 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
523 }
524 
525 snd_mixer ac97_mixer = {
526 	"AC97 mixer",
527 	ac97mix_init,
528 	ac97mix_uninit,
529 	ac97mix_reinit,
530 	ac97mix_set,
531 	ac97mix_setrecsrc,
532 };
533 
534