ac97.c revision 53465
150724Scg/*
250724Scg * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
350724Scg * All rights reserved.
450724Scg *
550724Scg * Redistribution and use in source and binary forms, with or without
650724Scg * modification, are permitted provided that the following conditions
750724Scg * are met:
850724Scg * 1. Redistributions of source code must retain the above copyright
950724Scg *    notice, this list of conditions and the following disclaimer.
1050724Scg * 2. Redistributions in binary form must reproduce the above copyright
1150724Scg *    notice, this list of conditions and the following disclaimer in the
1250724Scg *    documentation and/or other materials provided with the distribution.
1350724Scg *
1450724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1550724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1650724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1750724Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1850724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1950724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2050724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2150724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2250724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2350724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2450724Scg * SUCH DAMAGE.
2550724Scg *
2650733Speter * $FreeBSD: head/sys/dev/sound/pcm/ac97.c 53465 1999-11-20 16:50:33Z cg $
2750724Scg */
2850724Scg
2953465Scg#include <dev/sound/pcm/sound.h>
3053465Scg#include <dev/sound/pcm/ac97.h>
3150724Scg
3250724Scg#define AC97_MUTE	0x8000
3350724Scg
3450724Scg#define AC97_REG_RESET	0x00
3550724Scg#define AC97_MIX_MASTER	0x02
3650724Scg#define AC97_MIX_PHONES	0x04
3750724Scg#define AC97_MIX_MONO 	0x06
3850724Scg#define AC97_MIX_TONE	0x08
3950724Scg#define AC97_MIX_BEEP	0x0a
4050724Scg#define AC97_MIX_PHONE	0x0c
4150724Scg#define AC97_MIX_MIC	0x0e
4250724Scg#define AC97_MIX_LINE	0x10
4350724Scg#define AC97_MIX_CD	0x12
4450724Scg#define AC97_MIX_VIDEO	0x14
4550724Scg#define AC97_MIX_AUX	0x16
4650724Scg#define AC97_MIX_PCM	0x18
4750724Scg#define AC97_REG_RECSEL	0x1a
4850724Scg#define AC97_MIX_RGAIN	0x1c
4950724Scg#define AC97_MIX_MGAIN	0x1e
5050724Scg#define AC97_REG_GEN	0x20
5150724Scg#define AC97_REG_3D	0x22
5250724Scg#define AC97_REG_POWER	0x26
5350724Scg#define AC97_REG_ID1	0x7c
5450724Scg#define AC97_REG_ID2	0x7e
5550724Scg
5650724Scgstruct ac97mixtable_entry {
5750724Scg	int		reg:8;
5850724Scg	unsigned	bits:4;
5950724Scg	unsigned	ofs:4;
6050724Scg	unsigned	stereo:1;
6150724Scg	unsigned	mute:1;
6250724Scg	unsigned	recidx:4;
6350724Scg	unsigned        mask:1;
6450724Scg};
6550724Scg
6650724Scgstruct ac97_info {
6750724Scg	ac97_read *read;
6850724Scg	ac97_write *write;
6950724Scg	void *devinfo;
7050724Scg	char id[4];
7150724Scg	char rev;
7250724Scg	unsigned caps, se;
7350724Scg	struct ac97mixtable_entry mix[32];
7450724Scg};
7550724Scg
7650724Scgstruct ac97_codecid {
7750724Scg	u_int32_t id;
7850724Scg	char *name;
7950724Scg};
8050724Scg
8150724Scgstatic const struct ac97mixtable_entry ac97mixtable_default[32] = {
8250724Scg	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0 },
8350724Scg	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1 },
8450724Scg	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1 },
8550724Scg	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0 },
8650724Scg	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0 },
8750724Scg	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0 },
8850724Scg	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 0 },
8950724Scg	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0 },
9050724Scg	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0 },
9150724Scg	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0 },
9250724Scg	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0 }
9350724Scg};
9450724Scg
9550724Scgstatic const unsigned ac97mixdevs =
9650724Scg	SOUND_MASK_VOLUME |
9750724Scg	SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE |
9850724Scg	SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 |
9950724Scg	SOUND_MASK_VIDEO | SOUND_MASK_RECLEV;
10050724Scg
10150724Scgstatic const unsigned ac97recdevs =
10250724Scg	SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC |
10350724Scg	SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO;
10450724Scg
10550724Scgstatic struct ac97_codecid ac97codecid[] = {
10650724Scg	{ 0x414B4D00, "Asahi Kasei AK4540" 	},
10750724Scg	{ 0x43525900, "Cirrus Logic CS4297" 	},
10850724Scg	{ 0x83847600, "SigmaTel STAC????" 	},
10950724Scg	{ 0x83847604, "SigmaTel STAC9701/3/4/5" },
11050724Scg	{ 0x83847605, "SigmaTel STAC9704" 	},
11150724Scg	{ 0x83847608, "SigmaTel STAC9708" 	},
11250724Scg	{ 0x83847609, "SigmaTel STAC9721" 	},
11350724Scg	{ 0, 	      NULL			}
11450724Scg};
11550724Scg
11650724Scgstatic char *ac97enhancement[] = {
11750724Scg	"",
11850724Scg	"Analog Devices Phat Stereo",
11950724Scg	"Creative Stereo Enhancement",
12050724Scg	"National Semi 3D Stereo Enhancement",
12150724Scg	"Yamaha Ymersion",
12250724Scg	"BBE 3D Stereo Enhancement",
12350724Scg	"Crystal Semi 3D Stereo Enhancement",
12450724Scg	"Qsound QXpander",
12550724Scg	"Spatializer 3D Stereo Enhancement",
12650724Scg	"SRS 3D Stereo Enhancement",
12750724Scg	"Platform Tech 3D Stereo Enhancement",
12850724Scg	"AKM 3D Audio",
12950724Scg	"Aureal Stereo Enhancement",
13050724Scg	"Aztech 3D Enhancement",
13150724Scg	"Binaura 3D Audio Enhancement",
13250724Scg	"ESS Technology Stereo Enhancement",
13350724Scg	"Harman International VMAx",
13450724Scg	"Nvidea 3D Stereo Enhancement",
13550724Scg	"Philips Incredible Sound",
13650724Scg	"Texas Instruments 3D Stereo Enhancement",
13750724Scg	"VLSI Technology 3D Stereo Enhancement",
13850724Scg	"TriTech 3D Stereo Enhancement",
13950724Scg	"Realtek 3D Stereo Enhancement",
14050724Scg	"Samsung 3D Stereo Enhancement",
14150724Scg	"Wolfson Microelectronics 3D Enhancement",
14250724Scg	"Delta Integration 3D Enhancement",
14350724Scg	"SigmaTel 3D Enhancement",
14450724Scg	"Reserved 27",
14550724Scg	"Rockwell 3D Stereo Enhancement",
14650724Scg	"Reserved 29",
14750724Scg	"Reserved 30",
14850724Scg	"Reserved 31"
14950724Scg};
15050724Scg
15150724Scgstatic char *ac97feature[] = {
15250724Scg	"mic channel",
15350724Scg	"reserved",
15450724Scg	"tone",
15550724Scg	"simulated stereo",
15650724Scg	"headphone",
15750724Scg	"bass boost",
15850724Scg	"18 bit DAC",
15950724Scg	"20 bit DAC",
16050724Scg	"18 bit ADC",
16150724Scg	"20 bit ADC"
16250724Scg};
16350724Scg
16450724Scgstatic int
16550724Scgac97_setrecsrc(struct ac97_info *codec, int channel)
16650724Scg{
16750724Scg	struct ac97mixtable_entry *e = &codec->mix[channel];
16850724Scg	if (e->recidx > 0) {
16950724Scg		int val = e->recidx - 1;
17050724Scg		val |= val << 8;
17150724Scg		codec->write(codec->devinfo, AC97_REG_RECSEL, val);
17250724Scg		return 0;
17350724Scg	} else return -1;
17450724Scg}
17550724Scg
17650724Scgstatic int
17750724Scgac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
17850724Scg{
17950724Scg	struct ac97mixtable_entry *e = &codec->mix[channel];
18050724Scg	if (e->reg != 0) {
18150724Scg		int max, val;
18250724Scg
18350724Scg		if (!e->stereo) right = left;
18450724Scg		if (e->reg > 0) {
18550724Scg			left = 100 - left;
18650724Scg			right = 100 - right;
18750724Scg		}
18850724Scg
18950724Scg		max = (1 << e->bits) - 1;
19050724Scg		left = (left * max) / 100;
19150724Scg		right = (right * max) / 100;
19250724Scg
19350724Scg		val = (left << 8) | right;
19450724Scg
19550724Scg		left = (left * 100) / max;
19650724Scg		right = (right * 100) / max;
19750724Scg
19850724Scg		if (e->reg > 0) {
19950724Scg			left = 100 - left;
20050724Scg			right = 100 - right;
20150724Scg		}
20250724Scg
20350724Scg		if (!e->stereo) {
20450724Scg			val &= max;
20550724Scg			val <<= e->ofs;
20650724Scg			if (e->mask) {
20750724Scg				int cur = codec->read(codec->devinfo, e->reg);
20850724Scg				val |= cur & ~(max << e->ofs);
20950724Scg			}
21050724Scg		}
21150724Scg		if (left == 0 && right == 0 && e->mute == 1) val = AC97_MUTE;
21250724Scg		codec->write(codec->devinfo, abs(e->reg), val);
21350724Scg		return left | (right << 8);
21450724Scg	} else return -1;
21550724Scg}
21650724Scg
21750724Scg#if 0
21850724Scgstatic int
21950724Scgac97_getmixer(struct ac97_info *codec, int channel)
22050724Scg{
22150724Scg	struct ac97mixtable_entry *e = &codec->mix[channel];
22250724Scg	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
22350724Scg		int max, val, volume;
22450724Scg
22550724Scg		max = (1 << e->bits) - 1;
22650724Scg		val = codec->read(codec->devinfo, e->reg);
22750724Scg		if (val == AC97_MUTE && e->mute == 1) volume = 0;
22850724Scg		else {
22950724Scg			if (e->stereo == 0) val >>= e->ofs;
23050724Scg			val &= max;
23150724Scg			volume = (val * 100) / max;
23250724Scg			if (e->reg > 0) volume = 100 - volume;
23350724Scg		}
23450724Scg		return volume;
23550724Scg	} else return -1;
23650724Scg}
23750724Scg#endif
23850724Scg
23950724Scgstatic unsigned
24050724Scgac97_init(struct ac97_info *codec)
24150724Scg{
24250724Scg	unsigned i, j;
24350724Scg	u_int32_t id;
24450724Scg
24550724Scg	for (i = 0; i < 32; i++) codec->mix[i] = ac97mixtable_default[i];
24650724Scg
24750724Scg	codec->write(codec->devinfo, AC97_REG_POWER, 0);
24850724Scg	codec->write(codec->devinfo, AC97_REG_RESET, 0);
24950724Scg	DELAY(10000);
25050724Scg
25150724Scg	i = codec->read(codec->devinfo, AC97_REG_RESET);
25250724Scg	codec->caps = i & 0x03ff;
25350724Scg	codec->se =  (i & 0x7c00) >> 10;
25450724Scg
25550724Scg	id = (codec->read(codec->devinfo, AC97_REG_ID1) << 16) |
25650724Scg	      codec->read(codec->devinfo, AC97_REG_ID2);
25750724Scg	codec->rev = id & 0x000000ff;
25850724Scg
25950724Scg	codec->write(codec->devinfo, AC97_MIX_MASTER, 0x20);
26050724Scg	if ((codec->read(codec->devinfo, AC97_MIX_MASTER) & 0x20) == 0x20)
26150724Scg		codec->mix[SOUND_MIXER_VOLUME].bits++;
26250724Scg	codec->write(codec->devinfo, AC97_MIX_MASTER, 0x00);
26350724Scg
26450724Scg	if (bootverbose) {
26550724Scg		printf("ac97: codec id 0x%8x", id);
26650724Scg		for (i = 0; ac97codecid[i].id; i++) {
26750724Scg			if (ac97codecid[i].id == id) printf(" (%s)", ac97codecid[i].name);
26850724Scg		}
26950724Scg		printf("\nac97: codec features ");
27050724Scg		for (i = j = 0; i < 10; i++) {
27150724Scg			if (codec->caps & (1 << i)) {
27250724Scg				printf("%s%s", j? ", " : "", ac97feature[i]);
27350724Scg				j++;
27450724Scg			}
27550724Scg		}
27650724Scg		printf("%s%d bit master volume", j? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
27750724Scg		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
27850724Scg	}
27950724Scg
28050724Scg	if ((codec->read(codec->devinfo, AC97_REG_POWER) & 2) == 0)
28150724Scg		printf("ac97: dac not ready\n");
28250724Scg	return 0;
28350724Scg}
28450724Scg
28550724Scgstruct ac97_info *
28650724Scgac97_create(void *devinfo, ac97_read *rd, ac97_write *wr)
28750724Scg{
28850724Scg	struct ac97_info *codec;
28950724Scg
29050724Scg	codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT);
29150724Scg	if (codec != NULL) {
29250724Scg		codec->read = rd;
29350724Scg		codec->write = wr;
29450724Scg		codec->devinfo = devinfo;
29550724Scg	}
29650724Scg	return codec;
29750724Scg}
29850724Scg
29950724Scgstatic int
30050724Scgac97mix_init(snd_mixer *m)
30150724Scg{
30250724Scg	struct ac97_info *codec = mix_getdevinfo(m);
30350724Scg	if (codec == NULL) return -1;
30450724Scg	ac97_init(codec);
30550724Scg	mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0));
30650724Scg	mix_setrecdevs(m, ac97recdevs);
30750724Scg	return 0;
30850724Scg}
30950724Scg
31050724Scgstatic int
31150724Scgac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
31250724Scg{
31350724Scg	struct ac97_info *codec = mix_getdevinfo(m);
31450724Scg	if (codec == NULL) return -1;
31550724Scg	return ac97_setmixer(codec, dev, left, right);
31650724Scg}
31750724Scg
31850724Scgstatic int
31950724Scgac97mix_setrecsrc(snd_mixer *m, u_int32_t src)
32050724Scg{
32150724Scg	int i;
32250724Scg	struct ac97_info *codec = mix_getdevinfo(m);
32350724Scg	if (codec == NULL) return -1;
32450724Scg	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
32550724Scg		if ((src & (1 << i)) != 0) break;
32650724Scg	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
32750724Scg}
32850724Scg
32950724Scgsnd_mixer ac97_mixer = {
33050724Scg	"AC97 mixer",
33150724Scg	ac97mix_init,
33250724Scg	ac97mix_set,
33350724Scg	ac97mix_setrecsrc,
33450724Scg};
33550724Scg
336