xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision 4cf49a43559ed9fdad601bdcccd2c55963008675)
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/pcm/sound.h>
30 #include <dev/pcm/ac97.h>
31 
32 #define AC97_MUTE	0x8000
33 
34 #define AC97_REG_RESET	0x00
35 #define AC97_MIX_MASTER	0x02
36 #define AC97_MIX_PHONES	0x04
37 #define AC97_MIX_MONO 	0x06
38 #define AC97_MIX_TONE	0x08
39 #define AC97_MIX_BEEP	0x0a
40 #define AC97_MIX_PHONE	0x0c
41 #define AC97_MIX_MIC	0x0e
42 #define AC97_MIX_LINE	0x10
43 #define AC97_MIX_CD	0x12
44 #define AC97_MIX_VIDEO	0x14
45 #define AC97_MIX_AUX	0x16
46 #define AC97_MIX_PCM	0x18
47 #define AC97_REG_RECSEL	0x1a
48 #define AC97_MIX_RGAIN	0x1c
49 #define AC97_MIX_MGAIN	0x1e
50 #define AC97_REG_GEN	0x20
51 #define AC97_REG_3D	0x22
52 #define AC97_REG_POWER	0x26
53 #define AC97_REG_ID1	0x7c
54 #define AC97_REG_ID2	0x7e
55 
56 struct ac97mixtable_entry {
57 	int		reg:8;
58 	unsigned	bits:4;
59 	unsigned	ofs:4;
60 	unsigned	stereo:1;
61 	unsigned	mute:1;
62 	unsigned	recidx:4;
63 	unsigned        mask:1;
64 };
65 
66 struct ac97_info {
67 	ac97_read *read;
68 	ac97_write *write;
69 	void *devinfo;
70 	char id[4];
71 	char rev;
72 	unsigned caps, se;
73 	struct ac97mixtable_entry mix[32];
74 };
75 
76 struct ac97_codecid {
77 	u_int32_t id;
78 	char *name;
79 };
80 
81 static const struct ac97mixtable_entry ac97mixtable_default[32] = {
82 	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0 },
83 	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1 },
84 	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1 },
85 	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0 },
86 	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0 },
87 	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0 },
88 	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 0 },
89 	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0 },
90 	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0 },
91 	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0 },
92 	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0 }
93 };
94 
95 static const unsigned ac97mixdevs =
96 	SOUND_MASK_VOLUME |
97 	SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE |
98 	SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 |
99 	SOUND_MASK_VIDEO | SOUND_MASK_RECLEV;
100 
101 static const unsigned ac97recdevs =
102 	SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC |
103 	SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO;
104 
105 static struct ac97_codecid ac97codecid[] = {
106 	{ 0x414B4D00, "Asahi Kasei AK4540" 	},
107 	{ 0x43525900, "Cirrus Logic CS4297" 	},
108 	{ 0x83847600, "SigmaTel STAC????" 	},
109 	{ 0x83847604, "SigmaTel STAC9701/3/4/5" },
110 	{ 0x83847605, "SigmaTel STAC9704" 	},
111 	{ 0x83847608, "SigmaTel STAC9708" 	},
112 	{ 0x83847609, "SigmaTel STAC9721" 	},
113 	{ 0, 	      NULL			}
114 };
115 
116 static char *ac97enhancement[] = {
117 	"",
118 	"Analog Devices Phat Stereo",
119 	"Creative Stereo Enhancement",
120 	"National Semi 3D Stereo Enhancement",
121 	"Yamaha Ymersion",
122 	"BBE 3D Stereo Enhancement",
123 	"Crystal Semi 3D Stereo Enhancement",
124 	"Qsound QXpander",
125 	"Spatializer 3D Stereo Enhancement",
126 	"SRS 3D Stereo Enhancement",
127 	"Platform Tech 3D Stereo Enhancement",
128 	"AKM 3D Audio",
129 	"Aureal Stereo Enhancement",
130 	"Aztech 3D Enhancement",
131 	"Binaura 3D Audio Enhancement",
132 	"ESS Technology Stereo Enhancement",
133 	"Harman International VMAx",
134 	"Nvidea 3D Stereo Enhancement",
135 	"Philips Incredible Sound",
136 	"Texas Instruments 3D Stereo Enhancement",
137 	"VLSI Technology 3D Stereo Enhancement",
138 	"TriTech 3D Stereo Enhancement",
139 	"Realtek 3D Stereo Enhancement",
140 	"Samsung 3D Stereo Enhancement",
141 	"Wolfson Microelectronics 3D Enhancement",
142 	"Delta Integration 3D Enhancement",
143 	"SigmaTel 3D Enhancement",
144 	"Reserved 27",
145 	"Rockwell 3D Stereo Enhancement",
146 	"Reserved 29",
147 	"Reserved 30",
148 	"Reserved 31"
149 };
150 
151 static char *ac97feature[] = {
152 	"mic channel",
153 	"reserved",
154 	"tone",
155 	"simulated stereo",
156 	"headphone",
157 	"bass boost",
158 	"18 bit DAC",
159 	"20 bit DAC",
160 	"18 bit ADC",
161 	"20 bit ADC"
162 };
163 
164 static int
165 ac97_setrecsrc(struct ac97_info *codec, int channel)
166 {
167 	struct ac97mixtable_entry *e = &codec->mix[channel];
168 	if (e->recidx > 0) {
169 		int val = e->recidx - 1;
170 		val |= val << 8;
171 		codec->write(codec->devinfo, AC97_REG_RECSEL, val);
172 		return 0;
173 	} else return -1;
174 }
175 
176 static int
177 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
178 {
179 	struct ac97mixtable_entry *e = &codec->mix[channel];
180 	if (e->reg != 0) {
181 		int max, val;
182 
183 		if (!e->stereo) right = left;
184 		if (e->reg > 0) {
185 			left = 100 - left;
186 			right = 100 - right;
187 		}
188 
189 		max = (1 << e->bits) - 1;
190 		left = (left * max) / 100;
191 		right = (right * max) / 100;
192 
193 		val = (left << 8) | right;
194 
195 		left = (left * 100) / max;
196 		right = (right * 100) / max;
197 
198 		if (e->reg > 0) {
199 			left = 100 - left;
200 			right = 100 - right;
201 		}
202 
203 		if (!e->stereo) {
204 			val &= max;
205 			val <<= e->ofs;
206 			if (e->mask) {
207 				int cur = codec->read(codec->devinfo, e->reg);
208 				val |= cur & ~(max << e->ofs);
209 			}
210 		}
211 		if (left == 0 && right == 0 && e->mute == 1) val = AC97_MUTE;
212 		codec->write(codec->devinfo, abs(e->reg), val);
213 		return left | (right << 8);
214 	} else return -1;
215 }
216 
217 #if 0
218 static int
219 ac97_getmixer(struct ac97_info *codec, int channel)
220 {
221 	struct ac97mixtable_entry *e = &codec->mix[channel];
222 	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
223 		int max, val, volume;
224 
225 		max = (1 << e->bits) - 1;
226 		val = codec->read(codec->devinfo, e->reg);
227 		if (val == AC97_MUTE && e->mute == 1) volume = 0;
228 		else {
229 			if (e->stereo == 0) val >>= e->ofs;
230 			val &= max;
231 			volume = (val * 100) / max;
232 			if (e->reg > 0) volume = 100 - volume;
233 		}
234 		return volume;
235 	} else return -1;
236 }
237 #endif
238 
239 static unsigned
240 ac97_init(struct ac97_info *codec)
241 {
242 	unsigned i, j;
243 	u_int32_t id;
244 
245 	for (i = 0; i < 32; i++) codec->mix[i] = ac97mixtable_default[i];
246 
247 	codec->write(codec->devinfo, AC97_REG_POWER, 0);
248 	codec->write(codec->devinfo, AC97_REG_RESET, 0);
249 	DELAY(10000);
250 
251 	i = codec->read(codec->devinfo, AC97_REG_RESET);
252 	codec->caps = i & 0x03ff;
253 	codec->se =  (i & 0x7c00) >> 10;
254 
255 	id = (codec->read(codec->devinfo, AC97_REG_ID1) << 16) |
256 	      codec->read(codec->devinfo, AC97_REG_ID2);
257 	codec->rev = id & 0x000000ff;
258 
259 	codec->write(codec->devinfo, AC97_MIX_MASTER, 0x20);
260 	if ((codec->read(codec->devinfo, AC97_MIX_MASTER) & 0x20) == 0x20)
261 		codec->mix[SOUND_MIXER_VOLUME].bits++;
262 	codec->write(codec->devinfo, AC97_MIX_MASTER, 0x00);
263 
264 	if (bootverbose) {
265 		printf("ac97: codec id 0x%8x", id);
266 		for (i = 0; ac97codecid[i].id; i++) {
267 			if (ac97codecid[i].id == id) printf(" (%s)", ac97codecid[i].name);
268 		}
269 		printf("\nac97: codec features ");
270 		for (i = j = 0; i < 10; i++) {
271 			if (codec->caps & (1 << i)) {
272 				printf("%s%s", j? ", " : "", ac97feature[i]);
273 				j++;
274 			}
275 		}
276 		printf("%s%d bit master volume", j? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
277 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
278 	}
279 
280 	if ((codec->read(codec->devinfo, AC97_REG_POWER) & 2) == 0)
281 		printf("ac97: dac not ready\n");
282 	return 0;
283 }
284 
285 struct ac97_info *
286 ac97_create(void *devinfo, ac97_read *rd, ac97_write *wr)
287 {
288 	struct ac97_info *codec;
289 
290 	codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT);
291 	if (codec != NULL) {
292 		codec->read = rd;
293 		codec->write = wr;
294 		codec->devinfo = devinfo;
295 	}
296 	return codec;
297 }
298 
299 static int
300 ac97mix_init(snd_mixer *m)
301 {
302 	struct ac97_info *codec = mix_getdevinfo(m);
303 	if (codec == NULL) return -1;
304 	ac97_init(codec);
305 	mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0));
306 	mix_setrecdevs(m, ac97recdevs);
307 	return 0;
308 }
309 
310 static int
311 ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
312 {
313 	struct ac97_info *codec = mix_getdevinfo(m);
314 	if (codec == NULL) return -1;
315 	return ac97_setmixer(codec, dev, left, right);
316 }
317 
318 static int
319 ac97mix_setrecsrc(snd_mixer *m, u_int32_t src)
320 {
321 	int i;
322 	struct ac97_info *codec = mix_getdevinfo(m);
323 	if (codec == NULL) return -1;
324 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
325 		if ((src & (1 << i)) != 0) break;
326 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
327 }
328 
329 snd_mixer ac97_mixer = {
330 	"AC97 mixer",
331 	ac97mix_init,
332 	ac97mix_set,
333 	ac97mix_setrecsrc,
334 };
335 
336