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