ac97.c revision 53512
1299244Sjilles/*
2299244Sjilles * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3299244Sjilles * All rights reserved.
4299244Sjilles *
5299244Sjilles * Redistribution and use in source and binary forms, with or without
6299244Sjilles * modification, are permitted provided that the following conditions
7299244Sjilles * are met:
8299244Sjilles * 1. Redistributions of source code must retain the above copyright
9299244Sjilles *    notice, this list of conditions and the following disclaimer.
10299244Sjilles * 2. Redistributions in binary form must reproduce the above copyright
11299244Sjilles *    notice, this list of conditions and the following disclaimer in the
12299244Sjilles *    documentation and/or other materials provided with the distribution.
13299244Sjilles *
14299244Sjilles * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15299244Sjilles * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16299244Sjilles * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17299244Sjilles * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18299244Sjilles * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19299244Sjilles * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20299244Sjilles * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21299244Sjilles * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22299244Sjilles * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23299244Sjilles * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24299244Sjilles * SUCH DAMAGE.
25299244Sjilles *
26299244Sjilles * $FreeBSD: head/sys/dev/sound/pcm/ac97.c 53512 1999-11-21 17:15:12Z cg $
27299244Sjilles */
28299244Sjilles
29299244Sjilles#include <dev/sound/pcm/sound.h>
30299244Sjilles#include <dev/sound/pcm/ac97.h>
31299244Sjilles
32299244Sjilles#define AC97_MUTE	0x8000
33299244Sjilles
34299244Sjilles#define AC97_REG_RESET	0x00
35299244Sjilles#define AC97_MIX_MASTER	0x02
36299244Sjilles#define AC97_MIX_PHONES	0x04
37299244Sjilles#define AC97_MIX_MONO 	0x06
38299244Sjilles#define AC97_MIX_TONE	0x08
39299244Sjilles#define AC97_MIX_BEEP	0x0a
40299244Sjilles#define AC97_MIX_PHONE	0x0c
41299244Sjilles#define AC97_MIX_MIC	0x0e
42299244Sjilles#define AC97_MIX_LINE	0x10
43299244Sjilles#define AC97_MIX_CD	0x12
44299244Sjilles#define AC97_MIX_VIDEO	0x14
45299244Sjilles#define AC97_MIX_AUX	0x16
46299244Sjilles#define AC97_MIX_PCM	0x18
47299244Sjilles#define AC97_REG_RECSEL	0x1a
48299244Sjilles#define AC97_MIX_RGAIN	0x1c
49299244Sjilles#define AC97_MIX_MGAIN	0x1e
50299244Sjilles#define AC97_REG_GEN	0x20
51299244Sjilles#define AC97_REG_3D	0x22
52299244Sjilles#define AC97_REG_POWER	0x26
53299244Sjilles#define AC97_REG_ID1	0x7c
54299244Sjilles#define AC97_REG_ID2	0x7e
55299244Sjilles
56299244Sjillesstruct ac97mixtable_entry {
57299244Sjilles	int		reg:8;
58299244Sjilles	unsigned	bits:4;
59299244Sjilles	unsigned	ofs:4;
60299244Sjilles	unsigned	stereo:1;
61299244Sjilles	unsigned	mute:1;
62299244Sjilles	unsigned	recidx:4;
63299244Sjilles	unsigned        mask:1;
64299244Sjilles};
65299244Sjilles
66299244Sjillesstruct ac97_info {
67301765Sjilles	ac97_read *read;
68301765Sjilles	ac97_write *write;
69301765Sjilles	void *devinfo;
70301765Sjilles	char id[4];
71301765Sjilles	char rev;
72301765Sjilles	unsigned caps, se;
73299244Sjilles	struct ac97mixtable_entry mix[32];
74299244Sjilles};
75299244Sjilles
76299244Sjillesstruct ac97_codecid {
77299244Sjilles	u_int32_t id;
78299244Sjilles	char *name;
79299244Sjilles};
80299244Sjilles
81299244Sjillesstatic const struct ac97mixtable_entry ac97mixtable_default[32] = {
82299244Sjilles	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0 },
83299244Sjilles	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1 },
84299244Sjilles	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1 },
85299244Sjilles	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0 },
86299244Sjilles	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0 },
87299244Sjilles	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0 },
88299244Sjilles	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 0 },
89299244Sjilles	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0 },
90299244Sjilles	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0 },
91299244Sjilles	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0 },
92299244Sjilles	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0 }
93299244Sjilles};
94299244Sjilles
95299244Sjillesstatic const unsigned ac97mixdevs =
96299244Sjilles	SOUND_MASK_VOLUME |
97299244Sjilles	SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE |
98299244Sjilles	SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 |
99299244Sjilles	SOUND_MASK_VIDEO | SOUND_MASK_RECLEV;
100299244Sjilles
101299244Sjillesstatic const unsigned ac97recdevs =
102299244Sjilles	SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC |
103299244Sjilles	SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO;
104299244Sjilles
105299244Sjillesstatic struct ac97_codecid ac97codecid[] = {
106299244Sjilles	{ 0x414B4D00, "Asahi Kasei AK4540" 	},
107299244Sjilles	{ 0x43525900, "Cirrus Logic CS4297" 	},
108299244Sjilles	{ 0x83847600, "SigmaTel STAC????" 	},
109299244Sjilles	{ 0x83847604, "SigmaTel STAC9701/3/4/5" },
110299244Sjilles	{ 0x83847605, "SigmaTel STAC9704" 	},
111299244Sjilles	{ 0x83847608, "SigmaTel STAC9708" 	},
112299244Sjilles	{ 0x83847609, "SigmaTel STAC9721" 	},
113299244Sjilles	{ 0, 	      NULL			}
114299244Sjilles};
115299244Sjilles
116299244Sjillesstatic char *ac97enhancement[] = {
117299244Sjilles	"",
118299244Sjilles	"Analog Devices Phat Stereo",
119299244Sjilles	"Creative Stereo Enhancement",
120299244Sjilles	"National Semi 3D Stereo Enhancement",
121299244Sjilles	"Yamaha Ymersion",
122299244Sjilles	"BBE 3D Stereo Enhancement",
123299244Sjilles	"Crystal Semi 3D Stereo Enhancement",
124299244Sjilles	"Qsound QXpander",
125299244Sjilles	"Spatializer 3D Stereo Enhancement",
126299244Sjilles	"SRS 3D Stereo Enhancement",
127299244Sjilles	"Platform Tech 3D Stereo Enhancement",
128299244Sjilles	"AKM 3D Audio",
129299244Sjilles	"Aureal Stereo Enhancement",
130299244Sjilles	"Aztech 3D Enhancement",
131299244Sjilles	"Binaura 3D Audio Enhancement",
132299244Sjilles	"ESS Technology Stereo Enhancement",
133299244Sjilles	"Harman International VMAx",
134299244Sjilles	"Nvidea 3D Stereo Enhancement",
135299244Sjilles	"Philips Incredible Sound",
136299244Sjilles	"Texas Instruments 3D Stereo Enhancement",
137299244Sjilles	"VLSI Technology 3D Stereo Enhancement",
138299244Sjilles	"TriTech 3D Stereo Enhancement",
139299244Sjilles	"Realtek 3D Stereo Enhancement",
140299244Sjilles	"Samsung 3D Stereo Enhancement",
141299244Sjilles	"Wolfson Microelectronics 3D Enhancement",
142299244Sjilles	"Delta Integration 3D Enhancement",
143299244Sjilles	"SigmaTel 3D Enhancement",
144299244Sjilles	"Reserved 27",
145299244Sjilles	"Rockwell 3D Stereo Enhancement",
146299244Sjilles	"Reserved 29",
147299244Sjilles	"Reserved 30",
148299244Sjilles	"Reserved 31"
149299244Sjilles};
150299244Sjilles
151299244Sjillesstatic char *ac97feature[] = {
152299244Sjilles	"mic channel",
153299244Sjilles	"reserved",
154299244Sjilles	"tone",
155299244Sjilles	"simulated stereo",
156299244Sjilles	"headphone",
157299244Sjilles	"bass boost",
158299244Sjilles	"18 bit DAC",
159299244Sjilles	"20 bit DAC",
160299244Sjilles	"18 bit ADC",
161299244Sjilles	"20 bit ADC"
162299244Sjilles};
163299244Sjilles
164299244Sjillesstatic int
165299244Sjillesac97_setrecsrc(struct ac97_info *codec, int channel)
166299244Sjilles{
167299244Sjilles	struct ac97mixtable_entry *e = &codec->mix[channel];
168299244Sjilles	if (e->recidx > 0) {
169299244Sjilles		int val = e->recidx - 1;
170299244Sjilles		val |= val << 8;
171299244Sjilles		codec->write(codec->devinfo, AC97_REG_RECSEL, val);
172299244Sjilles		return 0;
173299244Sjilles	} else return -1;
174299244Sjilles}
175299244Sjilles
176299244Sjillesstatic int
177299244Sjillesac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
178299244Sjilles{
179299244Sjilles	struct ac97mixtable_entry *e = &codec->mix[channel];
180299244Sjilles	if (e->reg != 0) {
181299244Sjilles		int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
182299244Sjilles
183299244Sjilles		if (!e->stereo) right = left;
184299244Sjilles		if (e->reg > 0) {
185299244Sjilles			left = 100 - left;
186299244Sjilles			right = 100 - right;
187299244Sjilles		}
188299244Sjilles
189299244Sjilles		max = (1 << e->bits) - 1;
190299244Sjilles		left = (left * max) / 100;
191299244Sjilles		right = (right * max) / 100;
192299244Sjilles
193299244Sjilles		val = (left << 8) | right;
194299244Sjilles
195299244Sjilles		left = (left * 100) / max;
196299244Sjilles		right = (right * 100) / max;
197299244Sjilles
198299244Sjilles		if (e->reg > 0) {
199299244Sjilles			left = 100 - left;
200299244Sjilles			right = 100 - right;
201299244Sjilles		}
202299244Sjilles
203299244Sjilles		if (!e->stereo) {
204299244Sjilles			val &= max;
205299244Sjilles			val <<= e->ofs;
206299244Sjilles			if (e->mask) {
207299244Sjilles				int cur = codec->read(codec->devinfo, e->reg);
208299244Sjilles				val |= cur & ~(max << e->ofs);
209299244Sjilles			}
210299244Sjilles		}
211299244Sjilles		if (left == 0 && right == 0 && e->mute == 1) val = AC97_MUTE;
212299244Sjilles		codec->write(codec->devinfo, reg, val);
213299244Sjilles		return left | (right << 8);
214299244Sjilles	} else return -1;
215299244Sjilles}
216299244Sjilles
217299244Sjilles#if 0
218299244Sjillesstatic int
219299244Sjillesac97_getmixer(struct ac97_info *codec, int channel)
220299244Sjilles{
221299244Sjilles	struct ac97mixtable_entry *e = &codec->mix[channel];
222299244Sjilles	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
223299244Sjilles		int max, val, volume;
224299244Sjilles
225299244Sjilles		max = (1 << e->bits) - 1;
226299244Sjilles		val = codec->read(codec->devinfo, e->reg);
227299244Sjilles		if (val == AC97_MUTE && e->mute == 1) volume = 0;
228299244Sjilles		else {
229299244Sjilles			if (e->stereo == 0) val >>= e->ofs;
230299244Sjilles			val &= max;
231299244Sjilles			volume = (val * 100) / max;
232299244Sjilles			if (e->reg > 0) volume = 100 - volume;
233299244Sjilles		}
234299244Sjilles		return volume;
235299244Sjilles	} else return -1;
236299244Sjilles}
237299244Sjilles#endif
238299244Sjilles
239299244Sjillesstatic unsigned
240299244Sjillesac97_init(struct ac97_info *codec)
241299244Sjilles{
242299244Sjilles	unsigned i, j;
243299244Sjilles	u_int32_t id;
244299244Sjilles
245299244Sjilles	for (i = 0; i < 32; i++) codec->mix[i] = ac97mixtable_default[i];
246299244Sjilles
247299244Sjilles	codec->write(codec->devinfo, AC97_REG_POWER, 0);
248299244Sjilles	codec->write(codec->devinfo, AC97_REG_RESET, 0);
249299244Sjilles	DELAY(10000);
250299244Sjilles
251299244Sjilles	i = codec->read(codec->devinfo, AC97_REG_RESET);
252299244Sjilles	codec->caps = i & 0x03ff;
253299244Sjilles	codec->se =  (i & 0x7c00) >> 10;
254299244Sjilles
255299244Sjilles	id = (codec->read(codec->devinfo, AC97_REG_ID1) << 16) |
256299244Sjilles	      codec->read(codec->devinfo, AC97_REG_ID2);
257299244Sjilles	codec->rev = id & 0x000000ff;
258299244Sjilles
259299244Sjilles	codec->write(codec->devinfo, AC97_MIX_MASTER, 0x20);
260299244Sjilles	if ((codec->read(codec->devinfo, AC97_MIX_MASTER) & 0x20) == 0x20)
261299244Sjilles		codec->mix[SOUND_MIXER_VOLUME].bits++;
262299244Sjilles	codec->write(codec->devinfo, AC97_MIX_MASTER, 0x00);
263299244Sjilles
264299244Sjilles	if (bootverbose) {
265299244Sjilles		printf("ac97: codec id 0x%8x", id);
266299244Sjilles		for (i = 0; ac97codecid[i].id; i++) {
267299244Sjilles			if (ac97codecid[i].id == id) printf(" (%s)", ac97codecid[i].name);
268299244Sjilles		}
269299244Sjilles		printf("\nac97: codec features ");
270299244Sjilles		for (i = j = 0; i < 10; i++) {
271299244Sjilles			if (codec->caps & (1 << i)) {
272299244Sjilles				printf("%s%s", j? ", " : "", ac97feature[i]);
273299244Sjilles				j++;
274299244Sjilles			}
275299244Sjilles		}
276299244Sjilles		printf("%s%d bit master volume", j? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
277299244Sjilles		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
278299244Sjilles	}
279299244Sjilles
280299244Sjilles	if ((codec->read(codec->devinfo, AC97_REG_POWER) & 2) == 0)
281299244Sjilles		printf("ac97: dac not ready\n");
282299244Sjilles	return 0;
283299244Sjilles}
284299244Sjilles
285299244Sjillesstruct ac97_info *
286299244Sjillesac97_create(void *devinfo, ac97_read *rd, ac97_write *wr)
287299244Sjilles{
288299244Sjilles	struct ac97_info *codec;
289299244Sjilles
290299244Sjilles	codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT);
291299244Sjilles	if (codec != NULL) {
292299244Sjilles		codec->read = rd;
293299244Sjilles		codec->write = wr;
294299244Sjilles		codec->devinfo = devinfo;
295299244Sjilles	}
296299244Sjilles	return codec;
297299244Sjilles}
298299244Sjilles
299299244Sjillesstatic int
300299244Sjillesac97mix_init(snd_mixer *m)
301299244Sjilles{
302299244Sjilles	struct ac97_info *codec = mix_getdevinfo(m);
303299244Sjilles	if (codec == NULL) return -1;
304299244Sjilles	ac97_init(codec);
305299244Sjilles	mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0));
306299244Sjilles	mix_setrecdevs(m, ac97recdevs);
307299244Sjilles	return 0;
308299244Sjilles}
309299244Sjilles
310299244Sjillesstatic int
311299244Sjillesac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
312299244Sjilles{
313299244Sjilles	struct ac97_info *codec = mix_getdevinfo(m);
314299244Sjilles	if (codec == NULL) return -1;
315299244Sjilles	return ac97_setmixer(codec, dev, left, right);
316301765Sjilles}
317299244Sjilles
318299244Sjillesstatic int
319299244Sjillesac97mix_setrecsrc(snd_mixer *m, u_int32_t src)
320299244Sjilles{
321299244Sjilles	int i;
322299244Sjilles	struct ac97_info *codec = mix_getdevinfo(m);
323299244Sjilles	if (codec == NULL) return -1;
324299244Sjilles	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
325299244Sjilles		if ((src & (1 << i)) != 0) break;
326299244Sjilles	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
327299244Sjilles}
328299244Sjilles
329299244Sjillessnd_mixer ac97_mixer = {
330299244Sjilles	"AC97 mixer",
331299244Sjilles	ac97mix_init,
332299244Sjilles	ac97mix_set,
333299244Sjilles	ac97mix_setrecsrc,
334299244Sjilles};
335299244Sjilles
336299244Sjilles