xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision 03a00905d3fda61de51bda649687621605fde2ac)
1987e5972SCameron Grant /*
2987e5972SCameron Grant  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3987e5972SCameron Grant  * All rights reserved.
4987e5972SCameron Grant  *
5987e5972SCameron Grant  * Redistribution and use in source and binary forms, with or without
6987e5972SCameron Grant  * modification, are permitted provided that the following conditions
7987e5972SCameron Grant  * are met:
8987e5972SCameron Grant  * 1. Redistributions of source code must retain the above copyright
9987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer.
10987e5972SCameron Grant  * 2. Redistributions in binary form must reproduce the above copyright
11987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer in the
12987e5972SCameron Grant  *    documentation and/or other materials provided with the distribution.
13987e5972SCameron Grant  *
14987e5972SCameron Grant  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15987e5972SCameron Grant  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16987e5972SCameron Grant  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17987e5972SCameron Grant  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18987e5972SCameron Grant  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19987e5972SCameron Grant  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20987e5972SCameron Grant  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21987e5972SCameron Grant  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22987e5972SCameron Grant  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23987e5972SCameron Grant  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24987e5972SCameron Grant  * SUCH DAMAGE.
25987e5972SCameron Grant  *
2653c5a968SPeter Wemm  * $FreeBSD$
27987e5972SCameron Grant  */
28987e5972SCameron Grant 
29ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
30ef9308b1SCameron Grant #include <dev/sound/pcm/ac97.h>
31987e5972SCameron Grant 
32987e5972SCameron Grant #define AC97_MUTE	0x8000
33987e5972SCameron Grant 
34987e5972SCameron Grant #define AC97_REG_RESET	0x00
35987e5972SCameron Grant #define AC97_MIX_MASTER	0x02
36987e5972SCameron Grant #define AC97_MIX_PHONES	0x04
37987e5972SCameron Grant #define AC97_MIX_MONO 	0x06
38987e5972SCameron Grant #define AC97_MIX_TONE	0x08
39987e5972SCameron Grant #define AC97_MIX_BEEP	0x0a
40987e5972SCameron Grant #define AC97_MIX_PHONE	0x0c
41987e5972SCameron Grant #define AC97_MIX_MIC	0x0e
42987e5972SCameron Grant #define AC97_MIX_LINE	0x10
43987e5972SCameron Grant #define AC97_MIX_CD	0x12
44987e5972SCameron Grant #define AC97_MIX_VIDEO	0x14
45987e5972SCameron Grant #define AC97_MIX_AUX	0x16
46987e5972SCameron Grant #define AC97_MIX_PCM	0x18
47987e5972SCameron Grant #define AC97_REG_RECSEL	0x1a
48987e5972SCameron Grant #define AC97_MIX_RGAIN	0x1c
49987e5972SCameron Grant #define AC97_MIX_MGAIN	0x1e
50987e5972SCameron Grant #define AC97_REG_GEN	0x20
51987e5972SCameron Grant #define AC97_REG_3D	0x22
52987e5972SCameron Grant #define AC97_REG_POWER	0x26
53987e5972SCameron Grant #define AC97_REG_ID1	0x7c
54987e5972SCameron Grant #define AC97_REG_ID2	0x7e
55987e5972SCameron Grant 
56987e5972SCameron Grant struct ac97mixtable_entry {
57987e5972SCameron Grant 	int		reg:8;
58987e5972SCameron Grant 	unsigned	bits:4;
59987e5972SCameron Grant 	unsigned	ofs:4;
60987e5972SCameron Grant 	unsigned	stereo:1;
61987e5972SCameron Grant 	unsigned	mute:1;
62987e5972SCameron Grant 	unsigned	recidx:4;
63987e5972SCameron Grant 	unsigned        mask:1;
64987e5972SCameron Grant };
65987e5972SCameron Grant 
66987e5972SCameron Grant struct ac97_info {
6703a00905SCameron Grant 	device_t dev;
68987e5972SCameron Grant 	ac97_read *read;
69987e5972SCameron Grant 	ac97_write *write;
70987e5972SCameron Grant 	void *devinfo;
71987e5972SCameron Grant 	char id[4];
72987e5972SCameron Grant 	char rev;
73987e5972SCameron Grant 	unsigned caps, se;
74987e5972SCameron Grant 	struct ac97mixtable_entry mix[32];
75987e5972SCameron Grant };
76987e5972SCameron Grant 
77987e5972SCameron Grant struct ac97_codecid {
78987e5972SCameron Grant 	u_int32_t id;
79987e5972SCameron Grant 	char *name;
80987e5972SCameron Grant };
81987e5972SCameron Grant 
82987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = {
83987e5972SCameron Grant 	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0 },
84987e5972SCameron Grant 	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1 },
85987e5972SCameron Grant 	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1 },
86987e5972SCameron Grant 	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0 },
87987e5972SCameron Grant 	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0 },
88987e5972SCameron Grant 	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0 },
89987e5972SCameron Grant 	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 0 },
90987e5972SCameron Grant 	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0 },
91987e5972SCameron Grant 	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0 },
92987e5972SCameron Grant 	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0 },
93987e5972SCameron Grant 	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0 }
94987e5972SCameron Grant };
95987e5972SCameron Grant 
96987e5972SCameron Grant static const unsigned ac97mixdevs =
97987e5972SCameron Grant 	SOUND_MASK_VOLUME |
98987e5972SCameron Grant 	SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE |
99987e5972SCameron Grant 	SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 |
100987e5972SCameron Grant 	SOUND_MASK_VIDEO | SOUND_MASK_RECLEV;
101987e5972SCameron Grant 
102987e5972SCameron Grant static const unsigned ac97recdevs =
103987e5972SCameron Grant 	SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC |
104987e5972SCameron Grant 	SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO;
105987e5972SCameron Grant 
106987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = {
107987e5972SCameron Grant 	{ 0x414B4D00, "Asahi Kasei AK4540" 	},
108987e5972SCameron Grant 	{ 0x43525900, "Cirrus Logic CS4297" 	},
109987e5972SCameron Grant 	{ 0x83847600, "SigmaTel STAC????" 	},
110987e5972SCameron Grant 	{ 0x83847604, "SigmaTel STAC9701/3/4/5" },
111987e5972SCameron Grant 	{ 0x83847605, "SigmaTel STAC9704" 	},
112987e5972SCameron Grant 	{ 0x83847608, "SigmaTel STAC9708" 	},
113987e5972SCameron Grant 	{ 0x83847609, "SigmaTel STAC9721" 	},
114987e5972SCameron Grant 	{ 0, 	      NULL			}
115987e5972SCameron Grant };
116987e5972SCameron Grant 
117987e5972SCameron Grant static char *ac97enhancement[] = {
118987e5972SCameron Grant 	"",
119987e5972SCameron Grant 	"Analog Devices Phat Stereo",
120987e5972SCameron Grant 	"Creative Stereo Enhancement",
121987e5972SCameron Grant 	"National Semi 3D Stereo Enhancement",
122987e5972SCameron Grant 	"Yamaha Ymersion",
123987e5972SCameron Grant 	"BBE 3D Stereo Enhancement",
124987e5972SCameron Grant 	"Crystal Semi 3D Stereo Enhancement",
125987e5972SCameron Grant 	"Qsound QXpander",
126987e5972SCameron Grant 	"Spatializer 3D Stereo Enhancement",
127987e5972SCameron Grant 	"SRS 3D Stereo Enhancement",
128987e5972SCameron Grant 	"Platform Tech 3D Stereo Enhancement",
129987e5972SCameron Grant 	"AKM 3D Audio",
130987e5972SCameron Grant 	"Aureal Stereo Enhancement",
131987e5972SCameron Grant 	"Aztech 3D Enhancement",
132987e5972SCameron Grant 	"Binaura 3D Audio Enhancement",
133987e5972SCameron Grant 	"ESS Technology Stereo Enhancement",
134987e5972SCameron Grant 	"Harman International VMAx",
135987e5972SCameron Grant 	"Nvidea 3D Stereo Enhancement",
136987e5972SCameron Grant 	"Philips Incredible Sound",
137987e5972SCameron Grant 	"Texas Instruments 3D Stereo Enhancement",
138987e5972SCameron Grant 	"VLSI Technology 3D Stereo Enhancement",
139987e5972SCameron Grant 	"TriTech 3D Stereo Enhancement",
140987e5972SCameron Grant 	"Realtek 3D Stereo Enhancement",
141987e5972SCameron Grant 	"Samsung 3D Stereo Enhancement",
142987e5972SCameron Grant 	"Wolfson Microelectronics 3D Enhancement",
143987e5972SCameron Grant 	"Delta Integration 3D Enhancement",
144987e5972SCameron Grant 	"SigmaTel 3D Enhancement",
145987e5972SCameron Grant 	"Reserved 27",
146987e5972SCameron Grant 	"Rockwell 3D Stereo Enhancement",
147987e5972SCameron Grant 	"Reserved 29",
148987e5972SCameron Grant 	"Reserved 30",
149987e5972SCameron Grant 	"Reserved 31"
150987e5972SCameron Grant };
151987e5972SCameron Grant 
152987e5972SCameron Grant static char *ac97feature[] = {
153987e5972SCameron Grant 	"mic channel",
154987e5972SCameron Grant 	"reserved",
155987e5972SCameron Grant 	"tone",
156987e5972SCameron Grant 	"simulated stereo",
157987e5972SCameron Grant 	"headphone",
158987e5972SCameron Grant 	"bass boost",
159987e5972SCameron Grant 	"18 bit DAC",
160987e5972SCameron Grant 	"20 bit DAC",
161987e5972SCameron Grant 	"18 bit ADC",
162987e5972SCameron Grant 	"20 bit ADC"
163987e5972SCameron Grant };
164987e5972SCameron Grant 
165987e5972SCameron Grant static int
166987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel)
167987e5972SCameron Grant {
168987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
169987e5972SCameron Grant 	if (e->recidx > 0) {
170987e5972SCameron Grant 		int val = e->recidx - 1;
171987e5972SCameron Grant 		val |= val << 8;
172987e5972SCameron Grant 		codec->write(codec->devinfo, AC97_REG_RECSEL, val);
173987e5972SCameron Grant 		return 0;
174987e5972SCameron Grant 	} else return -1;
175987e5972SCameron Grant }
176987e5972SCameron Grant 
177987e5972SCameron Grant static int
178987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
179987e5972SCameron Grant {
180987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
181987e5972SCameron Grant 	if (e->reg != 0) {
182e479a8afSCameron Grant 		int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
183987e5972SCameron Grant 
184987e5972SCameron Grant 		if (!e->stereo) right = left;
185987e5972SCameron Grant 		if (e->reg > 0) {
186987e5972SCameron Grant 			left = 100 - left;
187987e5972SCameron Grant 			right = 100 - right;
188987e5972SCameron Grant 		}
189987e5972SCameron Grant 
190987e5972SCameron Grant 		max = (1 << e->bits) - 1;
191987e5972SCameron Grant 		left = (left * max) / 100;
192987e5972SCameron Grant 		right = (right * max) / 100;
193987e5972SCameron Grant 
194987e5972SCameron Grant 		val = (left << 8) | right;
195987e5972SCameron Grant 
196987e5972SCameron Grant 		left = (left * 100) / max;
197987e5972SCameron Grant 		right = (right * 100) / max;
198987e5972SCameron Grant 
199987e5972SCameron Grant 		if (e->reg > 0) {
200987e5972SCameron Grant 			left = 100 - left;
201987e5972SCameron Grant 			right = 100 - right;
202987e5972SCameron Grant 		}
203987e5972SCameron Grant 
204987e5972SCameron Grant 		if (!e->stereo) {
205987e5972SCameron Grant 			val &= max;
206987e5972SCameron Grant 			val <<= e->ofs;
207987e5972SCameron Grant 			if (e->mask) {
208987e5972SCameron Grant 				int cur = codec->read(codec->devinfo, e->reg);
209987e5972SCameron Grant 				val |= cur & ~(max << e->ofs);
210987e5972SCameron Grant 			}
211987e5972SCameron Grant 		}
212987e5972SCameron Grant 		if (left == 0 && right == 0 && e->mute == 1) val = AC97_MUTE;
213e479a8afSCameron Grant 		codec->write(codec->devinfo, reg, val);
214987e5972SCameron Grant 		return left | (right << 8);
215987e5972SCameron Grant 	} else return -1;
216987e5972SCameron Grant }
217987e5972SCameron Grant 
218987e5972SCameron Grant #if 0
219987e5972SCameron Grant static int
220987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel)
221987e5972SCameron Grant {
222987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
223987e5972SCameron Grant 	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
224987e5972SCameron Grant 		int max, val, volume;
225987e5972SCameron Grant 
226987e5972SCameron Grant 		max = (1 << e->bits) - 1;
227987e5972SCameron Grant 		val = codec->read(codec->devinfo, e->reg);
228987e5972SCameron Grant 		if (val == AC97_MUTE && e->mute == 1) volume = 0;
229987e5972SCameron Grant 		else {
230987e5972SCameron Grant 			if (e->stereo == 0) val >>= e->ofs;
231987e5972SCameron Grant 			val &= max;
232987e5972SCameron Grant 			volume = (val * 100) / max;
233987e5972SCameron Grant 			if (e->reg > 0) volume = 100 - volume;
234987e5972SCameron Grant 		}
235987e5972SCameron Grant 		return volume;
236987e5972SCameron Grant 	} else return -1;
237987e5972SCameron Grant }
238987e5972SCameron Grant #endif
239987e5972SCameron Grant 
240987e5972SCameron Grant static unsigned
241987e5972SCameron Grant ac97_init(struct ac97_info *codec)
242987e5972SCameron Grant {
243987e5972SCameron Grant 	unsigned i, j;
244987e5972SCameron Grant 	u_int32_t id;
245987e5972SCameron Grant 
246987e5972SCameron Grant 	for (i = 0; i < 32; i++) codec->mix[i] = ac97mixtable_default[i];
247987e5972SCameron Grant 
248987e5972SCameron Grant 	codec->write(codec->devinfo, AC97_REG_POWER, 0);
249987e5972SCameron Grant 	codec->write(codec->devinfo, AC97_REG_RESET, 0);
250987e5972SCameron Grant 	DELAY(10000);
251987e5972SCameron Grant 
252987e5972SCameron Grant 	i = codec->read(codec->devinfo, AC97_REG_RESET);
253987e5972SCameron Grant 	codec->caps = i & 0x03ff;
254987e5972SCameron Grant 	codec->se =  (i & 0x7c00) >> 10;
255987e5972SCameron Grant 
256987e5972SCameron Grant 	id = (codec->read(codec->devinfo, AC97_REG_ID1) << 16) |
257987e5972SCameron Grant 	      codec->read(codec->devinfo, AC97_REG_ID2);
258987e5972SCameron Grant 	codec->rev = id & 0x000000ff;
259987e5972SCameron Grant 
260987e5972SCameron Grant 	codec->write(codec->devinfo, AC97_MIX_MASTER, 0x20);
261987e5972SCameron Grant 	if ((codec->read(codec->devinfo, AC97_MIX_MASTER) & 0x20) == 0x20)
262987e5972SCameron Grant 		codec->mix[SOUND_MIXER_VOLUME].bits++;
263987e5972SCameron Grant 	codec->write(codec->devinfo, AC97_MIX_MASTER, 0x00);
264987e5972SCameron Grant 
265987e5972SCameron Grant 	if (bootverbose) {
26603a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec id 0x%8x", id);
267987e5972SCameron Grant 		for (i = 0; ac97codecid[i].id; i++) {
268987e5972SCameron Grant 			if (ac97codecid[i].id == id) printf(" (%s)", ac97codecid[i].name);
269987e5972SCameron Grant 		}
27003a00905SCameron Grant 		printf("\n");
27103a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec features ");
272987e5972SCameron Grant 		for (i = j = 0; i < 10; i++) {
273987e5972SCameron Grant 			if (codec->caps & (1 << i)) {
274987e5972SCameron Grant 				printf("%s%s", j? ", " : "", ac97feature[i]);
275987e5972SCameron Grant 				j++;
276987e5972SCameron Grant 			}
277987e5972SCameron Grant 		}
278987e5972SCameron Grant 		printf("%s%d bit master volume", j? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
279987e5972SCameron Grant 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
280987e5972SCameron Grant 	}
281987e5972SCameron Grant 
282987e5972SCameron Grant 	if ((codec->read(codec->devinfo, AC97_REG_POWER) & 2) == 0)
28303a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
284987e5972SCameron Grant 	return 0;
285987e5972SCameron Grant }
286987e5972SCameron Grant 
287987e5972SCameron Grant struct ac97_info *
28803a00905SCameron Grant ac97_create(device_t dev, void *devinfo, ac97_read *rd, ac97_write *wr)
289987e5972SCameron Grant {
290987e5972SCameron Grant 	struct ac97_info *codec;
291987e5972SCameron Grant 
292987e5972SCameron Grant 	codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT);
293987e5972SCameron Grant 	if (codec != NULL) {
29403a00905SCameron Grant 		codec->dev = dev;
295987e5972SCameron Grant 		codec->read = rd;
296987e5972SCameron Grant 		codec->write = wr;
297987e5972SCameron Grant 		codec->devinfo = devinfo;
298987e5972SCameron Grant 	}
299987e5972SCameron Grant 	return codec;
300987e5972SCameron Grant }
301987e5972SCameron Grant 
302987e5972SCameron Grant static int
303987e5972SCameron Grant ac97mix_init(snd_mixer *m)
304987e5972SCameron Grant {
305987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
306987e5972SCameron Grant 	if (codec == NULL) return -1;
307987e5972SCameron Grant 	ac97_init(codec);
308987e5972SCameron Grant 	mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0));
309987e5972SCameron Grant 	mix_setrecdevs(m, ac97recdevs);
310987e5972SCameron Grant 	return 0;
311987e5972SCameron Grant }
312987e5972SCameron Grant 
313987e5972SCameron Grant static int
314987e5972SCameron Grant ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
315987e5972SCameron Grant {
316987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
317987e5972SCameron Grant 	if (codec == NULL) return -1;
318987e5972SCameron Grant 	return ac97_setmixer(codec, dev, left, right);
319987e5972SCameron Grant }
320987e5972SCameron Grant 
321987e5972SCameron Grant static int
322987e5972SCameron Grant ac97mix_setrecsrc(snd_mixer *m, u_int32_t src)
323987e5972SCameron Grant {
324987e5972SCameron Grant 	int i;
325987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
326987e5972SCameron Grant 	if (codec == NULL) return -1;
327987e5972SCameron Grant 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
328987e5972SCameron Grant 		if ((src & (1 << i)) != 0) break;
329987e5972SCameron Grant 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
330987e5972SCameron Grant }
331987e5972SCameron Grant 
332987e5972SCameron Grant snd_mixer ac97_mixer = {
333987e5972SCameron Grant 	"AC97 mixer",
334987e5972SCameron Grant 	ac97mix_init,
335987e5972SCameron Grant 	ac97mix_set,
336987e5972SCameron Grant 	ac97mix_setrecsrc,
337987e5972SCameron Grant };
338987e5972SCameron Grant 
339