ac97.c revision 58384
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 58384 2000-03-20 15:30:50Z cg $
2750724Scg */
2850724Scg
2953465Scg#include <dev/sound/pcm/sound.h>
3053465Scg#include <dev/sound/pcm/ac97.h>
3150724Scg
3250724Scgstruct ac97mixtable_entry {
3350724Scg	int		reg:8;
3450724Scg	unsigned	bits:4;
3550724Scg	unsigned	ofs:4;
3650724Scg	unsigned	stereo:1;
3750724Scg	unsigned	mute:1;
3850724Scg	unsigned	recidx:4;
3950724Scg	unsigned        mask:1;
4050724Scg};
4150724Scg
4250724Scgstruct ac97_info {
4356249Scg	device_t dev;
4458384Scg	ac97_init *init;
4550724Scg	ac97_read *read;
4650724Scg	ac97_write *write;
4750724Scg	void *devinfo;
4850724Scg	char id[4];
4950724Scg	char rev;
5058384Scg	unsigned caps, se, extcaps, extid, extstat;
5150724Scg	struct ac97mixtable_entry mix[32];
5250724Scg};
5350724Scg
5450724Scgstruct ac97_codecid {
5550724Scg	u_int32_t id;
5650724Scg	char *name;
5750724Scg};
5850724Scg
5950724Scgstatic const struct ac97mixtable_entry ac97mixtable_default[32] = {
6050724Scg	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0 },
6150724Scg	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1 },
6250724Scg	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1 },
6350724Scg	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0 },
6450724Scg	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0 },
6550724Scg	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0 },
6650724Scg	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 0 },
6750724Scg	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0 },
6850724Scg	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0 },
6950724Scg	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0 },
7050724Scg	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0 }
7150724Scg};
7250724Scg
7350724Scgstatic const unsigned ac97mixdevs =
7450724Scg	SOUND_MASK_VOLUME |
7550724Scg	SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE |
7650724Scg	SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 |
7750724Scg	SOUND_MASK_VIDEO | SOUND_MASK_RECLEV;
7850724Scg
7950724Scgstatic const unsigned ac97recdevs =
8050724Scg	SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC |
8150724Scg	SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO;
8250724Scg
8350724Scgstatic struct ac97_codecid ac97codecid[] = {
8450724Scg	{ 0x414B4D00, "Asahi Kasei AK4540" 	},
8550724Scg	{ 0x43525900, "Cirrus Logic CS4297" 	},
8650724Scg	{ 0x83847600, "SigmaTel STAC????" 	},
8750724Scg	{ 0x83847604, "SigmaTel STAC9701/3/4/5" },
8850724Scg	{ 0x83847605, "SigmaTel STAC9704" 	},
8950724Scg	{ 0x83847608, "SigmaTel STAC9708" 	},
9050724Scg	{ 0x83847609, "SigmaTel STAC9721" 	},
9150724Scg	{ 0, 	      NULL			}
9250724Scg};
9350724Scg
9450724Scgstatic char *ac97enhancement[] = {
9550724Scg	"",
9650724Scg	"Analog Devices Phat Stereo",
9750724Scg	"Creative Stereo Enhancement",
9850724Scg	"National Semi 3D Stereo Enhancement",
9950724Scg	"Yamaha Ymersion",
10050724Scg	"BBE 3D Stereo Enhancement",
10150724Scg	"Crystal Semi 3D Stereo Enhancement",
10250724Scg	"Qsound QXpander",
10350724Scg	"Spatializer 3D Stereo Enhancement",
10450724Scg	"SRS 3D Stereo Enhancement",
10550724Scg	"Platform Tech 3D Stereo Enhancement",
10650724Scg	"AKM 3D Audio",
10750724Scg	"Aureal Stereo Enhancement",
10850724Scg	"Aztech 3D Enhancement",
10950724Scg	"Binaura 3D Audio Enhancement",
11050724Scg	"ESS Technology Stereo Enhancement",
11150724Scg	"Harman International VMAx",
11250724Scg	"Nvidea 3D Stereo Enhancement",
11350724Scg	"Philips Incredible Sound",
11450724Scg	"Texas Instruments 3D Stereo Enhancement",
11550724Scg	"VLSI Technology 3D Stereo Enhancement",
11650724Scg	"TriTech 3D Stereo Enhancement",
11750724Scg	"Realtek 3D Stereo Enhancement",
11850724Scg	"Samsung 3D Stereo Enhancement",
11950724Scg	"Wolfson Microelectronics 3D Enhancement",
12050724Scg	"Delta Integration 3D Enhancement",
12150724Scg	"SigmaTel 3D Enhancement",
12250724Scg	"Reserved 27",
12350724Scg	"Rockwell 3D Stereo Enhancement",
12450724Scg	"Reserved 29",
12550724Scg	"Reserved 30",
12650724Scg	"Reserved 31"
12750724Scg};
12850724Scg
12950724Scgstatic char *ac97feature[] = {
13050724Scg	"mic channel",
13150724Scg	"reserved",
13250724Scg	"tone",
13350724Scg	"simulated stereo",
13450724Scg	"headphone",
13550724Scg	"bass boost",
13650724Scg	"18 bit DAC",
13750724Scg	"20 bit DAC",
13850724Scg	"18 bit ADC",
13950724Scg	"20 bit ADC"
14050724Scg};
14150724Scg
14258384Scgstatic char *ac97extfeature[] = {
14358384Scg	"variable rate PCM",
14458384Scg	"double rate PCM",
14558384Scg	"reserved 1",
14658384Scg	"variable rate mic",
14758384Scg	"reserved 2",
14858384Scg	"reserved 3",
14958384Scg	"center DAC",
15058384Scg	"surround DAC",
15158384Scg	"LFE DAC",
15258384Scg	"AMAP",
15358384Scg	"reserved 4",
15458384Scg	"reserved 5",
15558384Scg	"reserved 6",
15658384Scg	"reserved 7",
15758384Scg};
15858384Scg
15958384Scgstatic u_int16_t
16058384Scgrdcd(struct ac97_info *codec, int reg)
16158384Scg{
16258384Scg	return codec->read(codec->devinfo, reg);
16358384Scg}
16458384Scg
16558384Scgstatic void
16658384Scgwrcd(struct ac97_info *codec, int reg, u_int16_t val)
16758384Scg{
16858384Scg	codec->write(codec->devinfo, reg, val);
16958384Scg}
17058384Scg
17158384Scgint
17258384Scgac97_setrate(struct ac97_info *codec, int which, int rate)
17358384Scg{
17458384Scg	u_int16_t v;
17558384Scg
17658384Scg	switch(which) {
17758384Scg	case AC97_REGEXT_FDACRATE:
17858384Scg	case AC97_REGEXT_SDACRATE:
17958384Scg	case AC97_REGEXT_LDACRATE:
18058384Scg	case AC97_REGEXT_LADCRATE:
18158384Scg	case AC97_REGEXT_MADCRATE:
18258384Scg		break;
18358384Scg
18458384Scg	default:
18558384Scg		return -1;
18658384Scg	}
18758384Scg
18858384Scg	if (rate != 0) {
18958384Scg		v = rate;
19058384Scg		if (codec->extstat & AC97_EXTCAP_DRA)
19158384Scg			v >>= 1;
19258384Scg		wrcd(codec, which, v);
19358384Scg	}
19458384Scg	v = rdcd(codec, which);
19558384Scg	if (codec->extstat & AC97_EXTCAP_DRA)
19658384Scg		v <<= 1;
19758384Scg	return v;
19858384Scg}
19958384Scg
20058384Scgint
20158384Scgac97_setextmode(struct ac97_info *codec, u_int16_t mode)
20258384Scg{
20358384Scg	mode &= AC97_EXTCAPS;
20458384Scg	if ((mode & ~codec->extcaps) != 0)
20558384Scg		return -1;
20658384Scg	wrcd(codec, AC97_REGEXT_STAT, mode);
20758384Scg	codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
20858384Scg	return (mode == codec->extstat)? 0 : -1;
20958384Scg}
21058384Scg
21150724Scgstatic int
21250724Scgac97_setrecsrc(struct ac97_info *codec, int channel)
21350724Scg{
21450724Scg	struct ac97mixtable_entry *e = &codec->mix[channel];
21550724Scg	if (e->recidx > 0) {
21650724Scg		int val = e->recidx - 1;
21750724Scg		val |= val << 8;
21858384Scg		wrcd(codec, AC97_REG_RECSEL, val);
21950724Scg		return 0;
22058384Scg	} else
22158384Scg		return -1;
22250724Scg}
22350724Scg
22450724Scgstatic int
22550724Scgac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
22650724Scg{
22750724Scg	struct ac97mixtable_entry *e = &codec->mix[channel];
22850724Scg	if (e->reg != 0) {
22953512Scg		int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
23050724Scg
23158384Scg		if (!e->stereo)
23258384Scg			right = left;
23350724Scg		if (e->reg > 0) {
23450724Scg			left = 100 - left;
23550724Scg			right = 100 - right;
23650724Scg		}
23750724Scg
23850724Scg		max = (1 << e->bits) - 1;
23950724Scg		left = (left * max) / 100;
24050724Scg		right = (right * max) / 100;
24150724Scg
24250724Scg		val = (left << 8) | right;
24350724Scg
24450724Scg		left = (left * 100) / max;
24550724Scg		right = (right * 100) / max;
24650724Scg
24750724Scg		if (e->reg > 0) {
24850724Scg			left = 100 - left;
24950724Scg			right = 100 - right;
25050724Scg		}
25150724Scg
25250724Scg		if (!e->stereo) {
25350724Scg			val &= max;
25450724Scg			val <<= e->ofs;
25550724Scg			if (e->mask) {
25658384Scg				int cur = rdcd(codec, e->reg);
25750724Scg				val |= cur & ~(max << e->ofs);
25850724Scg			}
25950724Scg		}
26058384Scg		if (left == 0 && right == 0 && e->mute == 1)
26158384Scg			val = AC97_MUTE;
26258384Scg		wrcd(codec, reg, val);
26350724Scg		return left | (right << 8);
26458384Scg	} else
26558384Scg		return -1;
26650724Scg}
26750724Scg
26850724Scg#if 0
26950724Scgstatic int
27050724Scgac97_getmixer(struct ac97_info *codec, int channel)
27150724Scg{
27250724Scg	struct ac97mixtable_entry *e = &codec->mix[channel];
27350724Scg	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
27450724Scg		int max, val, volume;
27550724Scg
27650724Scg		max = (1 << e->bits) - 1;
27758384Scg		val = rdcd(code, e->reg);
27858384Scg		if (val == AC97_MUTE && e->mute == 1)
27958384Scg			volume = 0;
28050724Scg		else {
28150724Scg			if (e->stereo == 0) val >>= e->ofs;
28250724Scg			val &= max;
28350724Scg			volume = (val * 100) / max;
28450724Scg			if (e->reg > 0) volume = 100 - volume;
28550724Scg		}
28650724Scg		return volume;
28758384Scg	} else
28858384Scg		return -1;
28950724Scg}
29050724Scg#endif
29150724Scg
29250724Scgstatic unsigned
29358384Scgac97_initmixer(struct ac97_info *codec)
29450724Scg{
29550724Scg	unsigned i, j;
29650724Scg	u_int32_t id;
29750724Scg
29858384Scg	for (i = 0; i < 32; i++)
29958384Scg		codec->mix[i] = ac97mixtable_default[i];
30050724Scg
30158384Scg	if (codec->init)
30258384Scg		codec->init(codec->devinfo);
30358384Scg	wrcd(codec, AC97_REG_POWER, 0);
30458384Scg	wrcd(codec, AC97_REG_RESET, 0);
30550724Scg	DELAY(10000);
30650724Scg
30758384Scg	i = rdcd(codec, AC97_REG_RESET);
30850724Scg	codec->caps = i & 0x03ff;
30950724Scg	codec->se =  (i & 0x7c00) >> 10;
31050724Scg
31158384Scg	i = rdcd(codec, AC97_REGEXT_ID);
31258384Scg	codec->extcaps = i & 0x3fff;
31358384Scg	codec->extid =  (i & 0xc000) >> 14;
31458384Scg
31558384Scg	codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
31658384Scg
31758384Scg	id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2);
31850724Scg	codec->rev = id & 0x000000ff;
31950724Scg
32058384Scg	wrcd(codec, AC97_MIX_MASTER, 0x20);
32158384Scg	if ((rdcd(codec, AC97_MIX_MASTER) & 0x20) == 0x20)
32250724Scg		codec->mix[SOUND_MIXER_VOLUME].bits++;
32358384Scg	wrcd(codec, AC97_MIX_MASTER, 0x00);
32450724Scg
32550724Scg	if (bootverbose) {
32656249Scg		device_printf(codec->dev, "ac97 codec id 0x%8x", id);
32758384Scg		for (i = 0; ac97codecid[i].id; i++)
32858384Scg			if (ac97codecid[i].id == id)
32958384Scg				printf(" (%s)", ac97codecid[i].name);
33056249Scg		printf("\n");
33156249Scg		device_printf(codec->dev, "ac97 codec features ");
33258384Scg		for (i = j = 0; i < 10; i++)
33358384Scg			if (codec->caps & (1 << i))
33458384Scg				printf("%s%s", j++? ", " : "", ac97feature[i]);
33558384Scg		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
33658384Scg		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
33758384Scg
33858384Scg		if (codec->extcaps != 0 || codec->extid) {
33958384Scg			device_printf(codec->dev, "ac97 %s codec",
34058384Scg				      codec->extid? "secondary" : "primary");
34158384Scg			if (codec->extcaps)
34258384Scg				printf(" extended features ");
34358384Scg			for (i = j = 0; i < 14; i++)
34458384Scg				if (codec->extcaps & (1 << i))
34558384Scg					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
34658384Scg			printf("\n");
34750724Scg		}
34850724Scg	}
34950724Scg
35058384Scg	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
35156249Scg		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
35250724Scg	return 0;
35350724Scg}
35450724Scg
35550724Scgstruct ac97_info *
35658384Scgac97_create(device_t dev, void *devinfo, ac97_init *init, ac97_read *rd, ac97_write *wr)
35750724Scg{
35850724Scg	struct ac97_info *codec;
35950724Scg
36050724Scg	codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT);
36150724Scg	if (codec != NULL) {
36256249Scg		codec->dev = dev;
36358384Scg		codec->init = init;
36450724Scg		codec->read = rd;
36550724Scg		codec->write = wr;
36650724Scg		codec->devinfo = devinfo;
36750724Scg	}
36850724Scg	return codec;
36950724Scg}
37050724Scg
37150724Scgstatic int
37250724Scgac97mix_init(snd_mixer *m)
37350724Scg{
37450724Scg	struct ac97_info *codec = mix_getdevinfo(m);
37558384Scg	if (codec == NULL)
37658384Scg		return -1;
37758384Scg	ac97_initmixer(codec);
37850724Scg	mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0));
37950724Scg	mix_setrecdevs(m, ac97recdevs);
38050724Scg	return 0;
38150724Scg}
38250724Scg
38350724Scgstatic int
38450724Scgac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
38550724Scg{
38650724Scg	struct ac97_info *codec = mix_getdevinfo(m);
38758384Scg	if (codec == NULL)
38858384Scg		return -1;
38950724Scg	return ac97_setmixer(codec, dev, left, right);
39050724Scg}
39150724Scg
39250724Scgstatic int
39350724Scgac97mix_setrecsrc(snd_mixer *m, u_int32_t src)
39450724Scg{
39550724Scg	int i;
39650724Scg	struct ac97_info *codec = mix_getdevinfo(m);
39758384Scg	if (codec == NULL)
39858384Scg		return -1;
39950724Scg	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
40058384Scg		if ((src & (1 << i)) != 0)
40158384Scg			break;
40250724Scg	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
40350724Scg}
40450724Scg
40550724Scgsnd_mixer ac97_mixer = {
40650724Scg	"AC97 mixer",
40750724Scg	ac97mix_init,
40850724Scg	ac97mix_set,
40950724Scg	ac97mix_setrecsrc,
41050724Scg};
41150724Scg
412