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