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