xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision 79bb7d52b37f05d7724bc1f816c564b57acd6eec)
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 
320f55ac6cSCameron Grant #include "mixer_if.h"
33987e5972SCameron Grant 
340f55ac6cSCameron Grant MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
35987e5972SCameron Grant 
3679bb7d52SCameron Grant struct ac97mixtable_entry {
3779bb7d52SCameron Grant 	int		reg:8;
3879bb7d52SCameron Grant 	unsigned	bits:4;
3979bb7d52SCameron Grant 	unsigned	ofs:4;
4079bb7d52SCameron Grant 	unsigned	stereo:1;
4179bb7d52SCameron Grant 	unsigned	mute:1;
4279bb7d52SCameron Grant 	unsigned	recidx:4;
4379bb7d52SCameron Grant 	unsigned        mask:1;
4479bb7d52SCameron Grant 	unsigned	enable:1;
4579bb7d52SCameron Grant };
4679bb7d52SCameron Grant 
4766ef8af5SCameron Grant #define AC97_NAMELEN	16
4866ef8af5SCameron Grant struct ac97_info {
4966ef8af5SCameron Grant 	kobj_t methods;
5066ef8af5SCameron Grant 	device_t dev;
5166ef8af5SCameron Grant 	void *devinfo;
5266ef8af5SCameron Grant 	char *id;
5366ef8af5SCameron Grant 	char rev;
5466ef8af5SCameron Grant 	unsigned count, caps, se, extcaps, extid, extstat, noext:1;
5579bb7d52SCameron Grant 	u_int32_t flags;
5666ef8af5SCameron Grant 	struct ac97mixtable_entry mix[32];
5766ef8af5SCameron Grant 	char name[AC97_NAMELEN];
5866ef8af5SCameron Grant 	void *lock;
5966ef8af5SCameron Grant };
6066ef8af5SCameron Grant 
61987e5972SCameron Grant struct ac97_codecid {
626b4b88f7SCameron Grant 	u_int32_t id, noext:1;
63987e5972SCameron Grant 	char *name;
64987e5972SCameron Grant };
65987e5972SCameron Grant 
66987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = {
67341f16ccSCameron Grant 	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0, 1 },
68341f16ccSCameron Grant 	[SOUND_MIXER_MONITOR]	= { AC97_MIX_PHONES, 	5, 0, 1, 1, 0, 0, 0 },
69341f16ccSCameron Grant 	[SOUND_MIXER_PHONEOUT]	= { AC97_MIX_MONO, 	5, 0, 0, 1, 7, 0, 0 },
70341f16ccSCameron Grant 	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1, 0 },
71341f16ccSCameron Grant 	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1, 0 },
72341f16ccSCameron Grant 	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0, 1 },
73341f16ccSCameron Grant 	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0, 0 },
74341f16ccSCameron Grant 	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0, 1 },
75341f16ccSCameron Grant 	[SOUND_MIXER_PHONEIN]	= { AC97_MIX_PHONE, 	5, 0, 0, 1, 8, 0, 0 },
76341f16ccSCameron Grant 	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 0, 1 },
77341f16ccSCameron Grant 	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
78341f16ccSCameron Grant 	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
79341f16ccSCameron Grant 	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
80341f16ccSCameron Grant 	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
81987e5972SCameron Grant };
82987e5972SCameron Grant 
83987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = {
8473770282SCameron Grant 	{ 0x41445303, 0, "Analog Devices AD1819" },
8573770282SCameron Grant 	{ 0x41445340, 0, "Analog Devices AD1881" },
8673770282SCameron Grant 	{ 0x41445348, 0, "Analog Devices AD1881A" },
8773770282SCameron Grant 	{ 0x41445360, 0, "Analog Devices AD1885" },
8873770282SCameron Grant 	{ 0x414b4d00, 1, "Asahi Kasei AK4540" },
8973770282SCameron Grant 	{ 0x414b4d01, 1, "Asahi Kasei AK4542" },
9073770282SCameron Grant 	{ 0x414b4d02, 1, "Asahi Kasei AK4543" },
9173770282SCameron Grant 	{ 0x414c4710, 0, "Avance Logic ALC200/200P" },
926b4b88f7SCameron Grant 	{ 0x43525900, 0, "Cirrus Logic CS4297" },
9373770282SCameron Grant 	{ 0x43525903, 0, "Cirrus Logic CS4297" },
9473770282SCameron Grant 	{ 0x43525913, 0, "Cirrus Logic CS4297A" },
9573770282SCameron Grant 	{ 0x43525914, 0, "Cirrus Logic CS4297B" },
9673770282SCameron Grant 	{ 0x43525923, 0, "Cirrus Logic CS4294C" },
9773770282SCameron Grant 	{ 0x4352592b, 0, "Cirrus Logic CS4298C" },
9873770282SCameron Grant 	{ 0x43525931, 0, "Cirrus Logic CS4299A" },
9973770282SCameron Grant 	{ 0x43525933, 0, "Cirrus Logic CS4299C" },
10073770282SCameron Grant 	{ 0x43525934, 0, "Cirrus Logic CS4299D" },
10173770282SCameron Grant 	{ 0x43525941, 0, "Cirrus Logic CS4201A" },
10273770282SCameron Grant 	{ 0x43525951, 0, "Cirrus Logic CS4205A" },
10373770282SCameron Grant 	{ 0x43525961, 0, "Cirrus Logic CS4291A" },
10473770282SCameron Grant 	{ 0x45838308, 0, "ESS Technology ES1921" },
10573770282SCameron Grant 	{ 0x49434511, 0, "ICEnsemble ICE1232" },
10673770282SCameron Grant 	{ 0x4e534331, 0, "National Semiconductor LM4549" },
10773770282SCameron Grant 	{ 0x83847600, 0, "SigmaTel STAC9700/9783/9784" },
10873770282SCameron Grant 	{ 0x83847604, 0, "SigmaTel STAC9701/9703/9704/9705" },
1096b4b88f7SCameron Grant 	{ 0x83847605, 0, "SigmaTel STAC9704" },
11073770282SCameron Grant 	{ 0x83847608, 0, "SigmaTel STAC9708/9711" },
11173770282SCameron Grant 	{ 0x83847609, 0, "SigmaTel STAC9721/9723" },
11273770282SCameron Grant 	{ 0x83847644, 0, "SigmaTel STAC9744" },
11373770282SCameron Grant 	{ 0x83847656, 0, "SigmaTel STAC9756/9757" },
11473770282SCameron Grant 	{ 0x53494c22, 0, "Silicon Laboratory Si3036" },
11573770282SCameron Grant 	{ 0x53494c23, 0, "Silicon Laboratory Si3038" },
11673770282SCameron Grant 	{ 0x54524103, 0, "TriTech TR?????" },
11773770282SCameron Grant 	{ 0x54524106, 0, "TriTech TR28026" },
11873770282SCameron Grant 	{ 0x54524108, 0, "TriTech TR28028" },
11973770282SCameron Grant 	{ 0x54524123, 0, "TriTech TR28602" },
12073770282SCameron Grant 	{ 0x574d4c00, 0, "Wolfson WM9701A" },
12173770282SCameron Grant 	{ 0x574d4c03, 0, "Wolfson WM9703/9704" },
12273770282SCameron Grant 	{ 0x574d4c04, 0, "Wolfson WM9704 (quad)" },
1236b4b88f7SCameron Grant 	{ 0, 0, NULL }
124987e5972SCameron Grant };
125987e5972SCameron Grant 
126987e5972SCameron Grant static char *ac97enhancement[] = {
12704553e63SCameron Grant 	"no 3D Stereo Enhancement",
128987e5972SCameron Grant 	"Analog Devices Phat Stereo",
129987e5972SCameron Grant 	"Creative Stereo Enhancement",
130987e5972SCameron Grant 	"National Semi 3D Stereo Enhancement",
131987e5972SCameron Grant 	"Yamaha Ymersion",
132987e5972SCameron Grant 	"BBE 3D Stereo Enhancement",
133987e5972SCameron Grant 	"Crystal Semi 3D Stereo Enhancement",
134987e5972SCameron Grant 	"Qsound QXpander",
135987e5972SCameron Grant 	"Spatializer 3D Stereo Enhancement",
136987e5972SCameron Grant 	"SRS 3D Stereo Enhancement",
137987e5972SCameron Grant 	"Platform Tech 3D Stereo Enhancement",
138987e5972SCameron Grant 	"AKM 3D Audio",
139987e5972SCameron Grant 	"Aureal Stereo Enhancement",
140987e5972SCameron Grant 	"Aztech 3D Enhancement",
141987e5972SCameron Grant 	"Binaura 3D Audio Enhancement",
142987e5972SCameron Grant 	"ESS Technology Stereo Enhancement",
143987e5972SCameron Grant 	"Harman International VMAx",
144987e5972SCameron Grant 	"Nvidea 3D Stereo Enhancement",
145987e5972SCameron Grant 	"Philips Incredible Sound",
146987e5972SCameron Grant 	"Texas Instruments 3D Stereo Enhancement",
147987e5972SCameron Grant 	"VLSI Technology 3D Stereo Enhancement",
148987e5972SCameron Grant 	"TriTech 3D Stereo Enhancement",
149987e5972SCameron Grant 	"Realtek 3D Stereo Enhancement",
150987e5972SCameron Grant 	"Samsung 3D Stereo Enhancement",
151987e5972SCameron Grant 	"Wolfson Microelectronics 3D Enhancement",
152987e5972SCameron Grant 	"Delta Integration 3D Enhancement",
153987e5972SCameron Grant 	"SigmaTel 3D Enhancement",
154987e5972SCameron Grant 	"Reserved 27",
155987e5972SCameron Grant 	"Rockwell 3D Stereo Enhancement",
156987e5972SCameron Grant 	"Reserved 29",
157987e5972SCameron Grant 	"Reserved 30",
158987e5972SCameron Grant 	"Reserved 31"
159987e5972SCameron Grant };
160987e5972SCameron Grant 
161987e5972SCameron Grant static char *ac97feature[] = {
162987e5972SCameron Grant 	"mic channel",
163987e5972SCameron Grant 	"reserved",
164987e5972SCameron Grant 	"tone",
165987e5972SCameron Grant 	"simulated stereo",
166987e5972SCameron Grant 	"headphone",
167987e5972SCameron Grant 	"bass boost",
168987e5972SCameron Grant 	"18 bit DAC",
169987e5972SCameron Grant 	"20 bit DAC",
170987e5972SCameron Grant 	"18 bit ADC",
171987e5972SCameron Grant 	"20 bit ADC"
172987e5972SCameron Grant };
173987e5972SCameron Grant 
17439004e69SCameron Grant static char *ac97extfeature[] = {
17539004e69SCameron Grant 	"variable rate PCM",
17639004e69SCameron Grant 	"double rate PCM",
17739004e69SCameron Grant 	"reserved 1",
17839004e69SCameron Grant 	"variable rate mic",
17939004e69SCameron Grant 	"reserved 2",
18039004e69SCameron Grant 	"reserved 3",
18139004e69SCameron Grant 	"center DAC",
18239004e69SCameron Grant 	"surround DAC",
18339004e69SCameron Grant 	"LFE DAC",
18439004e69SCameron Grant 	"AMAP",
18539004e69SCameron Grant 	"reserved 4",
18639004e69SCameron Grant 	"reserved 5",
18739004e69SCameron Grant 	"reserved 6",
18839004e69SCameron Grant 	"reserved 7",
18939004e69SCameron Grant };
19039004e69SCameron Grant 
19139004e69SCameron Grant static u_int16_t
19239004e69SCameron Grant rdcd(struct ac97_info *codec, int reg)
19339004e69SCameron Grant {
1940f55ac6cSCameron Grant 	return AC97_READ(codec->methods, codec->devinfo, reg);
19539004e69SCameron Grant }
19639004e69SCameron Grant 
19739004e69SCameron Grant static void
19839004e69SCameron Grant wrcd(struct ac97_info *codec, int reg, u_int16_t val)
19939004e69SCameron Grant {
2000f55ac6cSCameron Grant 	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
20139004e69SCameron Grant }
20239004e69SCameron Grant 
20339004e69SCameron Grant int
20439004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate)
20539004e69SCameron Grant {
20639004e69SCameron Grant 	u_int16_t v;
20739004e69SCameron Grant 
20839004e69SCameron Grant 	switch(which) {
20939004e69SCameron Grant 	case AC97_REGEXT_FDACRATE:
21039004e69SCameron Grant 	case AC97_REGEXT_SDACRATE:
21139004e69SCameron Grant 	case AC97_REGEXT_LDACRATE:
21239004e69SCameron Grant 	case AC97_REGEXT_LADCRATE:
21339004e69SCameron Grant 	case AC97_REGEXT_MADCRATE:
21439004e69SCameron Grant 		break;
21539004e69SCameron Grant 
21639004e69SCameron Grant 	default:
21739004e69SCameron Grant 		return -1;
21839004e69SCameron Grant 	}
21939004e69SCameron Grant 
22066ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
22139004e69SCameron Grant 	if (rate != 0) {
22239004e69SCameron Grant 		v = rate;
22339004e69SCameron Grant 		if (codec->extstat & AC97_EXTCAP_DRA)
22439004e69SCameron Grant 			v >>= 1;
22539004e69SCameron Grant 		wrcd(codec, which, v);
22639004e69SCameron Grant 	}
22739004e69SCameron Grant 	v = rdcd(codec, which);
22839004e69SCameron Grant 	if (codec->extstat & AC97_EXTCAP_DRA)
22939004e69SCameron Grant 		v <<= 1;
23066ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
23139004e69SCameron Grant 	return v;
23239004e69SCameron Grant }
23339004e69SCameron Grant 
23439004e69SCameron Grant int
23539004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
23639004e69SCameron Grant {
23739004e69SCameron Grant 	mode &= AC97_EXTCAPS;
23839004e69SCameron Grant 	if ((mode & ~codec->extcaps) != 0)
23939004e69SCameron Grant 		return -1;
24066ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
24139004e69SCameron Grant 	wrcd(codec, AC97_REGEXT_STAT, mode);
24239004e69SCameron Grant 	codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
24366ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
24439004e69SCameron Grant 	return (mode == codec->extstat)? 0 : -1;
24539004e69SCameron Grant }
24639004e69SCameron Grant 
2479ec437a3SCameron Grant u_int16_t
2489ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec)
2499ec437a3SCameron Grant {
2509ec437a3SCameron Grant 	return codec->extstat;
2519ec437a3SCameron Grant }
2529ec437a3SCameron Grant 
2539ec437a3SCameron Grant u_int16_t
2549ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec)
2559ec437a3SCameron Grant {
2569ec437a3SCameron Grant 	return codec->extcaps;
2579ec437a3SCameron Grant }
2589ec437a3SCameron Grant 
259987e5972SCameron Grant static int
260987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel)
261987e5972SCameron Grant {
262987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
263341f16ccSCameron Grant 
264987e5972SCameron Grant 	if (e->recidx > 0) {
265987e5972SCameron Grant 		int val = e->recidx - 1;
266987e5972SCameron Grant 		val |= val << 8;
26766ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
26839004e69SCameron Grant 		wrcd(codec, AC97_REG_RECSEL, val);
26966ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
270987e5972SCameron Grant 		return 0;
27139004e69SCameron Grant 	} else
27239004e69SCameron Grant 		return -1;
273987e5972SCameron Grant }
274987e5972SCameron Grant 
275987e5972SCameron Grant static int
276987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
277987e5972SCameron Grant {
278987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
279341f16ccSCameron Grant 
280341f16ccSCameron Grant 	if (e->reg && e->enable && e->bits) {
281e479a8afSCameron Grant 		int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
282987e5972SCameron Grant 
28339004e69SCameron Grant 		if (!e->stereo)
28439004e69SCameron Grant 			right = left;
285987e5972SCameron Grant 		if (e->reg > 0) {
286987e5972SCameron Grant 			left = 100 - left;
287987e5972SCameron Grant 			right = 100 - right;
288987e5972SCameron Grant 		}
289987e5972SCameron Grant 
290987e5972SCameron Grant 		max = (1 << e->bits) - 1;
291987e5972SCameron Grant 		left = (left * max) / 100;
292987e5972SCameron Grant 		right = (right * max) / 100;
293987e5972SCameron Grant 
294987e5972SCameron Grant 		val = (left << 8) | right;
295987e5972SCameron Grant 
296987e5972SCameron Grant 		left = (left * 100) / max;
297987e5972SCameron Grant 		right = (right * 100) / max;
298987e5972SCameron Grant 
299987e5972SCameron Grant 		if (e->reg > 0) {
300987e5972SCameron Grant 			left = 100 - left;
301987e5972SCameron Grant 			right = 100 - right;
302987e5972SCameron Grant 		}
303987e5972SCameron Grant 
304987e5972SCameron Grant 		if (!e->stereo) {
305987e5972SCameron Grant 			val &= max;
306987e5972SCameron Grant 			val <<= e->ofs;
307987e5972SCameron Grant 			if (e->mask) {
30839004e69SCameron Grant 				int cur = rdcd(codec, e->reg);
309987e5972SCameron Grant 				val |= cur & ~(max << e->ofs);
310987e5972SCameron Grant 			}
311987e5972SCameron Grant 		}
31239004e69SCameron Grant 		if (left == 0 && right == 0 && e->mute == 1)
31339004e69SCameron Grant 			val = AC97_MUTE;
31466ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
31539004e69SCameron Grant 		wrcd(codec, reg, val);
31666ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
317987e5972SCameron Grant 		return left | (right << 8);
318341f16ccSCameron Grant 	} else {
319341f16ccSCameron Grant 		/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
32039004e69SCameron Grant 		return -1;
321987e5972SCameron Grant 	}
322341f16ccSCameron Grant }
323987e5972SCameron Grant 
324987e5972SCameron Grant #if 0
325987e5972SCameron Grant static int
326987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel)
327987e5972SCameron Grant {
328987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
329987e5972SCameron Grant 	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
330987e5972SCameron Grant 		int max, val, volume;
331987e5972SCameron Grant 
332987e5972SCameron Grant 		max = (1 << e->bits) - 1;
33339004e69SCameron Grant 		val = rdcd(code, e->reg);
33439004e69SCameron Grant 		if (val == AC97_MUTE && e->mute == 1)
33539004e69SCameron Grant 			volume = 0;
336987e5972SCameron Grant 		else {
337987e5972SCameron Grant 			if (e->stereo == 0) val >>= e->ofs;
338987e5972SCameron Grant 			val &= max;
339987e5972SCameron Grant 			volume = (val * 100) / max;
340987e5972SCameron Grant 			if (e->reg > 0) volume = 100 - volume;
341987e5972SCameron Grant 		}
342987e5972SCameron Grant 		return volume;
34339004e69SCameron Grant 	} else
34439004e69SCameron Grant 		return -1;
345987e5972SCameron Grant }
346987e5972SCameron Grant #endif
347987e5972SCameron Grant 
348987e5972SCameron Grant static unsigned
34939004e69SCameron Grant ac97_initmixer(struct ac97_info *codec)
350987e5972SCameron Grant {
351341f16ccSCameron Grant 	unsigned i, j, k, old;
352987e5972SCameron Grant 	u_int32_t id;
353987e5972SCameron Grant 
35466ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
35539004e69SCameron Grant 	for (i = 0; i < 32; i++)
35639004e69SCameron Grant 		codec->mix[i] = ac97mixtable_default[i];
357987e5972SCameron Grant 
3580f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
359cd2c103aSCameron Grant 	if (codec->count == 0) {
36004553e63SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
36166ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
36204553e63SCameron Grant 		return ENODEV;
36304553e63SCameron Grant 	}
3649ec437a3SCameron Grant 
36579bb7d52SCameron Grant 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
36639004e69SCameron Grant 	wrcd(codec, AC97_REG_RESET, 0);
367e620d959SCameron Grant 	DELAY(100000);
36879bb7d52SCameron Grant 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
369987e5972SCameron Grant 
37039004e69SCameron Grant 	i = rdcd(codec, AC97_REG_RESET);
371987e5972SCameron Grant 	codec->caps = i & 0x03ff;
372987e5972SCameron Grant 	codec->se =  (i & 0x7c00) >> 10;
373987e5972SCameron Grant 
3746b4b88f7SCameron Grant 	id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2);
3756b4b88f7SCameron Grant 	codec->rev = id & 0x000000ff;
376e620d959SCameron Grant 	if (id == 0 || id == 0xffffffff) {
377e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
37866ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
379e620d959SCameron Grant 		return ENODEV;
380e620d959SCameron Grant 	}
3816b4b88f7SCameron Grant 
382cd2c103aSCameron Grant 	codec->noext = 0;
38366ef8af5SCameron Grant 	codec->id = NULL;
384cd2c103aSCameron Grant 	for (i = 0; ac97codecid[i].id; i++) {
385cd2c103aSCameron Grant 		if (ac97codecid[i].id == id) {
38666ef8af5SCameron Grant 			codec->id = ac97codecid[i].name;
387cd2c103aSCameron Grant 			codec->noext = ac97codecid[i].noext;
388cd2c103aSCameron Grant 		}
389cd2c103aSCameron Grant 	}
3906b4b88f7SCameron Grant 
391cd2c103aSCameron Grant 	codec->extcaps = 0;
392cd2c103aSCameron Grant 	codec->extid = 0;
393cd2c103aSCameron Grant 	codec->extstat = 0;
3946a6ee5bbSCameron Grant 	if (!codec->noext) {
39539004e69SCameron Grant 		i = rdcd(codec, AC97_REGEXT_ID);
3966a6ee5bbSCameron Grant 		if (i != 0xffff) {
39739004e69SCameron Grant 			codec->extcaps = i & 0x3fff;
39839004e69SCameron Grant 			codec->extid =  (i & 0xc000) >> 14;
39939004e69SCameron Grant 			codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
4006b4b88f7SCameron Grant 		}
4016a6ee5bbSCameron Grant 	}
402987e5972SCameron Grant 
403341f16ccSCameron Grant 	for (i = 0; i < 32; i++) {
40433c878f0SCameron Grant 		k = codec->noext? codec->mix[i].enable : 1;
40533c878f0SCameron Grant 		if (k && (codec->mix[i].reg > 0)) {
406341f16ccSCameron Grant 			old = rdcd(codec, codec->mix[i].reg);
407341f16ccSCameron Grant 			wrcd(codec, codec->mix[i].reg, 0x3f);
408341f16ccSCameron Grant 			j = rdcd(codec, codec->mix[i].reg);
409341f16ccSCameron Grant 			wrcd(codec, codec->mix[i].reg, old);
4106a6ee5bbSCameron Grant 			codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
411341f16ccSCameron Grant 			for (k = 1; j & (1 << k); k++);
412341f16ccSCameron Grant 			codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
413341f16ccSCameron Grant 		}
414341f16ccSCameron Grant 		/* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
415341f16ccSCameron Grant 	}
416987e5972SCameron Grant 
417987e5972SCameron Grant 	if (bootverbose) {
418e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec id 0x%08x", id);
41966ef8af5SCameron Grant 		if (codec->id)
42066ef8af5SCameron Grant 			printf(" (%s)", codec->id);
42103a00905SCameron Grant 		printf("\n");
42203a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec features ");
42339004e69SCameron Grant 		for (i = j = 0; i < 10; i++)
42439004e69SCameron Grant 			if (codec->caps & (1 << i))
42539004e69SCameron Grant 				printf("%s%s", j++? ", " : "", ac97feature[i]);
42639004e69SCameron Grant 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
427987e5972SCameron Grant 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
42839004e69SCameron Grant 
42939004e69SCameron Grant 		if (codec->extcaps != 0 || codec->extid) {
43039004e69SCameron Grant 			device_printf(codec->dev, "ac97 %s codec",
43139004e69SCameron Grant 				      codec->extid? "secondary" : "primary");
43239004e69SCameron Grant 			if (codec->extcaps)
43339004e69SCameron Grant 				printf(" extended features ");
43439004e69SCameron Grant 			for (i = j = 0; i < 14; i++)
43539004e69SCameron Grant 				if (codec->extcaps & (1 << i))
43639004e69SCameron Grant 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
43739004e69SCameron Grant 			printf("\n");
43839004e69SCameron Grant 		}
439987e5972SCameron Grant 	}
440987e5972SCameron Grant 
44139004e69SCameron Grant 	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
44203a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
44366ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
444987e5972SCameron Grant 	return 0;
445987e5972SCameron Grant }
446987e5972SCameron Grant 
4479ec437a3SCameron Grant static unsigned
4489ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec)
4499ec437a3SCameron Grant {
4509ec437a3SCameron Grant 	unsigned i;
4519ec437a3SCameron Grant 
45266ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
4530f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
4549ec437a3SCameron Grant 	if (codec->count == 0) {
4559ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
45666ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
4579ec437a3SCameron Grant 		return ENODEV;
4589ec437a3SCameron Grant 	}
4599ec437a3SCameron Grant 
46079bb7d52SCameron Grant 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
4619ec437a3SCameron Grant 	wrcd(codec, AC97_REG_RESET, 0);
4629ec437a3SCameron Grant 	DELAY(100000);
46379bb7d52SCameron Grant 	wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
4649ec437a3SCameron Grant 	i = rdcd(codec, AC97_REG_RESET);
4659ec437a3SCameron Grant 
4669ec437a3SCameron Grant 	if (!codec->noext) {
4679ec437a3SCameron Grant 		wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
4689ec437a3SCameron Grant 		if (rdcd(codec, AC97_REGEXT_STAT) != codec->extstat)
4699ec437a3SCameron Grant 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
4709ec437a3SCameron Grant 				      codec->extstat, rdcd(codec, AC97_REGEXT_STAT));
4719ec437a3SCameron Grant 	}
4729ec437a3SCameron Grant 
4739ec437a3SCameron Grant 	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
4749ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
47566ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
4769ec437a3SCameron Grant 	return 0;
4779ec437a3SCameron Grant }
4789ec437a3SCameron Grant 
479987e5972SCameron Grant struct ac97_info *
4800f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
481987e5972SCameron Grant {
482987e5972SCameron Grant 	struct ac97_info *codec;
483987e5972SCameron Grant 
4840f55ac6cSCameron Grant 	codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
4850f55ac6cSCameron Grant 	if (codec == NULL)
4860f55ac6cSCameron Grant 		return NULL;
4870f55ac6cSCameron Grant 
48866ef8af5SCameron Grant 	snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
48966ef8af5SCameron Grant 	codec->lock = snd_mtxcreate(codec->name);
4900f55ac6cSCameron Grant 	codec->methods = kobj_create(cls, M_AC97, M_WAITOK);
4910f55ac6cSCameron Grant 	if (codec->methods == NULL) {
49266ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
49366ef8af5SCameron Grant 		snd_mtxfree(codec->lock);
4940f55ac6cSCameron Grant 		free(codec, M_AC97);
4950f55ac6cSCameron Grant 		return NULL;
496987e5972SCameron Grant 	}
4970f55ac6cSCameron Grant 
4980f55ac6cSCameron Grant 	codec->dev = dev;
4990f55ac6cSCameron Grant 	codec->devinfo = devinfo;
50079bb7d52SCameron Grant 	codec->flags = 0;
501987e5972SCameron Grant 	return codec;
502987e5972SCameron Grant }
503987e5972SCameron Grant 
50433dbf14aSCameron Grant void
50533dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec)
50633dbf14aSCameron Grant {
50766ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
5080f55ac6cSCameron Grant 	if (codec->methods != NULL)
5090f55ac6cSCameron Grant 		kobj_delete(codec->methods, M_AC97);
51066ef8af5SCameron Grant 	snd_mtxfree(codec->lock);
5110f55ac6cSCameron Grant 	free(codec, M_AC97);
51233dbf14aSCameron Grant }
51333dbf14aSCameron Grant 
51479bb7d52SCameron Grant void
51579bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val)
51679bb7d52SCameron Grant {
51779bb7d52SCameron Grant 	codec->flags = val;
51879bb7d52SCameron Grant }
51979bb7d52SCameron Grant 
52079bb7d52SCameron Grant u_int32_t
52179bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec)
52279bb7d52SCameron Grant {
52379bb7d52SCameron Grant 	return codec->flags;
52479bb7d52SCameron Grant }
52579bb7d52SCameron Grant 
5260f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
5270f55ac6cSCameron Grant 
528987e5972SCameron Grant static int
52966ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m)
530987e5972SCameron Grant {
531987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
532341f16ccSCameron Grant 	u_int32_t i, mask;
533341f16ccSCameron Grant 
53439004e69SCameron Grant 	if (codec == NULL)
53539004e69SCameron Grant 		return -1;
536341f16ccSCameron Grant 
537e620d959SCameron Grant 	if (ac97_initmixer(codec))
538e620d959SCameron Grant 		return -1;
539341f16ccSCameron Grant 
540341f16ccSCameron Grant 	mask = 0;
541341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
542341f16ccSCameron Grant 		mask |= codec->mix[i].enable? 1 << i : 0;
543341f16ccSCameron Grant 	mix_setdevs(m, mask);
544341f16ccSCameron Grant 
545341f16ccSCameron Grant 	mask = 0;
546341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
547341f16ccSCameron Grant 		mask |= codec->mix[i].recidx? 1 << i : 0;
548341f16ccSCameron Grant 	mix_setrecdevs(m, mask);
549987e5972SCameron Grant 	return 0;
550987e5972SCameron Grant }
551987e5972SCameron Grant 
552987e5972SCameron Grant static int
55366ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m)
55433dbf14aSCameron Grant {
55533dbf14aSCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
556341f16ccSCameron Grant 
55733dbf14aSCameron Grant 	if (codec == NULL)
55833dbf14aSCameron Grant 		return -1;
55933dbf14aSCameron Grant 	/*
56033dbf14aSCameron Grant 	if (ac97_uninitmixer(codec))
56133dbf14aSCameron Grant 		return -1;
56233dbf14aSCameron Grant 	*/
56333dbf14aSCameron Grant 	ac97_destroy(codec);
56433dbf14aSCameron Grant 	return 0;
56533dbf14aSCameron Grant }
56633dbf14aSCameron Grant 
56733dbf14aSCameron Grant static int
56866ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m)
5699ec437a3SCameron Grant {
5709ec437a3SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
5719ec437a3SCameron Grant 
5729ec437a3SCameron Grant 	if (codec == NULL)
5739ec437a3SCameron Grant 		return -1;
5749ec437a3SCameron Grant 	return ac97_reinitmixer(codec);
5759ec437a3SCameron Grant }
5769ec437a3SCameron Grant 
5779ec437a3SCameron Grant static int
57866ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
579987e5972SCameron Grant {
580987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
581341f16ccSCameron Grant 
58239004e69SCameron Grant 	if (codec == NULL)
58339004e69SCameron Grant 		return -1;
584987e5972SCameron Grant 	return ac97_setmixer(codec, dev, left, right);
585987e5972SCameron Grant }
586987e5972SCameron Grant 
587987e5972SCameron Grant static int
58866ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
589987e5972SCameron Grant {
590987e5972SCameron Grant 	int i;
591987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
592341f16ccSCameron Grant 
59339004e69SCameron Grant 	if (codec == NULL)
59439004e69SCameron Grant 		return -1;
595987e5972SCameron Grant 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
59639004e69SCameron Grant 		if ((src & (1 << i)) != 0)
59739004e69SCameron Grant 			break;
598987e5972SCameron Grant 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
599987e5972SCameron Grant }
600987e5972SCameron Grant 
6010f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = {
6020f55ac6cSCameron Grant     	KOBJMETHOD(mixer_init,		ac97mix_init),
6030f55ac6cSCameron Grant     	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
6040f55ac6cSCameron Grant     	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
6050f55ac6cSCameron Grant     	KOBJMETHOD(mixer_set,		ac97mix_set),
6060f55ac6cSCameron Grant     	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
6070f55ac6cSCameron Grant 	{ 0, 0 }
608987e5972SCameron Grant };
6090f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer);
6100f55ac6cSCameron Grant 
6110f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
6120f55ac6cSCameron Grant 
6130f55ac6cSCameron Grant kobj_class_t
6140f55ac6cSCameron Grant ac97_getmixerclass(void)
6150f55ac6cSCameron Grant {
6160f55ac6cSCameron Grant 	return &ac97mixer_class;
6170f55ac6cSCameron Grant }
6180f55ac6cSCameron Grant 
619987e5972SCameron Grant 
620