xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision 66ef8af5b0e00b074572e81d3ff8c0453b284325)
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 
3666ef8af5SCameron Grant #define AC97_NAMELEN	16
3766ef8af5SCameron Grant struct ac97_info {
3866ef8af5SCameron Grant 	kobj_t methods;
3966ef8af5SCameron Grant 	device_t dev;
4066ef8af5SCameron Grant 	void *devinfo;
4166ef8af5SCameron Grant 	char *id;
4266ef8af5SCameron Grant 	char rev;
4366ef8af5SCameron Grant 	unsigned count, caps, se, extcaps, extid, extstat, noext:1;
4466ef8af5SCameron Grant 	struct ac97mixtable_entry mix[32];
4566ef8af5SCameron Grant 	char name[AC97_NAMELEN];
4666ef8af5SCameron Grant 	void *lock;
4766ef8af5SCameron Grant };
4866ef8af5SCameron Grant 
49987e5972SCameron Grant struct ac97_codecid {
506b4b88f7SCameron Grant 	u_int32_t id, noext:1;
51987e5972SCameron Grant 	char *name;
52987e5972SCameron Grant };
53987e5972SCameron Grant 
54987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = {
55341f16ccSCameron Grant 	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0, 1 },
56341f16ccSCameron Grant 	[SOUND_MIXER_MONITOR]	= { AC97_MIX_PHONES, 	5, 0, 1, 1, 0, 0, 0 },
57341f16ccSCameron Grant 	[SOUND_MIXER_PHONEOUT]	= { AC97_MIX_MONO, 	5, 0, 0, 1, 7, 0, 0 },
58341f16ccSCameron Grant 	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1, 0 },
59341f16ccSCameron Grant 	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1, 0 },
60341f16ccSCameron Grant 	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0, 1 },
61341f16ccSCameron Grant 	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0, 0 },
62341f16ccSCameron Grant 	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0, 1 },
63341f16ccSCameron Grant 	[SOUND_MIXER_PHONEIN]	= { AC97_MIX_PHONE, 	5, 0, 0, 1, 8, 0, 0 },
64341f16ccSCameron Grant 	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 0, 1 },
65341f16ccSCameron Grant 	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
66341f16ccSCameron Grant 	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
67341f16ccSCameron Grant 	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
68341f16ccSCameron Grant 	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
69987e5972SCameron Grant };
70987e5972SCameron Grant 
71987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = {
7273770282SCameron Grant 	{ 0x41445303, 0, "Analog Devices AD1819" },
7373770282SCameron Grant 	{ 0x41445340, 0, "Analog Devices AD1881" },
7473770282SCameron Grant 	{ 0x41445348, 0, "Analog Devices AD1881A" },
7573770282SCameron Grant 	{ 0x41445360, 0, "Analog Devices AD1885" },
7673770282SCameron Grant 	{ 0x414b4d00, 1, "Asahi Kasei AK4540" },
7773770282SCameron Grant 	{ 0x414b4d01, 1, "Asahi Kasei AK4542" },
7873770282SCameron Grant 	{ 0x414b4d02, 1, "Asahi Kasei AK4543" },
7973770282SCameron Grant 	{ 0x414c4710, 0, "Avance Logic ALC200/200P" },
806b4b88f7SCameron Grant 	{ 0x43525900, 0, "Cirrus Logic CS4297" },
8173770282SCameron Grant 	{ 0x43525903, 0, "Cirrus Logic CS4297" },
8273770282SCameron Grant 	{ 0x43525913, 0, "Cirrus Logic CS4297A" },
8373770282SCameron Grant 	{ 0x43525914, 0, "Cirrus Logic CS4297B" },
8473770282SCameron Grant 	{ 0x43525923, 0, "Cirrus Logic CS4294C" },
8573770282SCameron Grant 	{ 0x4352592b, 0, "Cirrus Logic CS4298C" },
8673770282SCameron Grant 	{ 0x43525931, 0, "Cirrus Logic CS4299A" },
8773770282SCameron Grant 	{ 0x43525933, 0, "Cirrus Logic CS4299C" },
8873770282SCameron Grant 	{ 0x43525934, 0, "Cirrus Logic CS4299D" },
8973770282SCameron Grant 	{ 0x43525941, 0, "Cirrus Logic CS4201A" },
9073770282SCameron Grant 	{ 0x43525951, 0, "Cirrus Logic CS4205A" },
9173770282SCameron Grant 	{ 0x43525961, 0, "Cirrus Logic CS4291A" },
9273770282SCameron Grant 	{ 0x45838308, 0, "ESS Technology ES1921" },
9373770282SCameron Grant 	{ 0x49434511, 0, "ICEnsemble ICE1232" },
9473770282SCameron Grant 	{ 0x4e534331, 0, "National Semiconductor LM4549" },
9573770282SCameron Grant 	{ 0x83847600, 0, "SigmaTel STAC9700/9783/9784" },
9673770282SCameron Grant 	{ 0x83847604, 0, "SigmaTel STAC9701/9703/9704/9705" },
976b4b88f7SCameron Grant 	{ 0x83847605, 0, "SigmaTel STAC9704" },
9873770282SCameron Grant 	{ 0x83847608, 0, "SigmaTel STAC9708/9711" },
9973770282SCameron Grant 	{ 0x83847609, 0, "SigmaTel STAC9721/9723" },
10073770282SCameron Grant 	{ 0x83847644, 0, "SigmaTel STAC9744" },
10173770282SCameron Grant 	{ 0x83847656, 0, "SigmaTel STAC9756/9757" },
10273770282SCameron Grant 	{ 0x53494c22, 0, "Silicon Laboratory Si3036" },
10373770282SCameron Grant 	{ 0x53494c23, 0, "Silicon Laboratory Si3038" },
10473770282SCameron Grant 	{ 0x54524103, 0, "TriTech TR?????" },
10573770282SCameron Grant 	{ 0x54524106, 0, "TriTech TR28026" },
10673770282SCameron Grant 	{ 0x54524108, 0, "TriTech TR28028" },
10773770282SCameron Grant 	{ 0x54524123, 0, "TriTech TR28602" },
10873770282SCameron Grant 	{ 0x574d4c00, 0, "Wolfson WM9701A" },
10973770282SCameron Grant 	{ 0x574d4c03, 0, "Wolfson WM9703/9704" },
11073770282SCameron Grant 	{ 0x574d4c04, 0, "Wolfson WM9704 (quad)" },
1116b4b88f7SCameron Grant 	{ 0, 0, NULL }
112987e5972SCameron Grant };
113987e5972SCameron Grant 
114987e5972SCameron Grant static char *ac97enhancement[] = {
11504553e63SCameron Grant 	"no 3D Stereo Enhancement",
116987e5972SCameron Grant 	"Analog Devices Phat Stereo",
117987e5972SCameron Grant 	"Creative Stereo Enhancement",
118987e5972SCameron Grant 	"National Semi 3D Stereo Enhancement",
119987e5972SCameron Grant 	"Yamaha Ymersion",
120987e5972SCameron Grant 	"BBE 3D Stereo Enhancement",
121987e5972SCameron Grant 	"Crystal Semi 3D Stereo Enhancement",
122987e5972SCameron Grant 	"Qsound QXpander",
123987e5972SCameron Grant 	"Spatializer 3D Stereo Enhancement",
124987e5972SCameron Grant 	"SRS 3D Stereo Enhancement",
125987e5972SCameron Grant 	"Platform Tech 3D Stereo Enhancement",
126987e5972SCameron Grant 	"AKM 3D Audio",
127987e5972SCameron Grant 	"Aureal Stereo Enhancement",
128987e5972SCameron Grant 	"Aztech 3D Enhancement",
129987e5972SCameron Grant 	"Binaura 3D Audio Enhancement",
130987e5972SCameron Grant 	"ESS Technology Stereo Enhancement",
131987e5972SCameron Grant 	"Harman International VMAx",
132987e5972SCameron Grant 	"Nvidea 3D Stereo Enhancement",
133987e5972SCameron Grant 	"Philips Incredible Sound",
134987e5972SCameron Grant 	"Texas Instruments 3D Stereo Enhancement",
135987e5972SCameron Grant 	"VLSI Technology 3D Stereo Enhancement",
136987e5972SCameron Grant 	"TriTech 3D Stereo Enhancement",
137987e5972SCameron Grant 	"Realtek 3D Stereo Enhancement",
138987e5972SCameron Grant 	"Samsung 3D Stereo Enhancement",
139987e5972SCameron Grant 	"Wolfson Microelectronics 3D Enhancement",
140987e5972SCameron Grant 	"Delta Integration 3D Enhancement",
141987e5972SCameron Grant 	"SigmaTel 3D Enhancement",
142987e5972SCameron Grant 	"Reserved 27",
143987e5972SCameron Grant 	"Rockwell 3D Stereo Enhancement",
144987e5972SCameron Grant 	"Reserved 29",
145987e5972SCameron Grant 	"Reserved 30",
146987e5972SCameron Grant 	"Reserved 31"
147987e5972SCameron Grant };
148987e5972SCameron Grant 
149987e5972SCameron Grant static char *ac97feature[] = {
150987e5972SCameron Grant 	"mic channel",
151987e5972SCameron Grant 	"reserved",
152987e5972SCameron Grant 	"tone",
153987e5972SCameron Grant 	"simulated stereo",
154987e5972SCameron Grant 	"headphone",
155987e5972SCameron Grant 	"bass boost",
156987e5972SCameron Grant 	"18 bit DAC",
157987e5972SCameron Grant 	"20 bit DAC",
158987e5972SCameron Grant 	"18 bit ADC",
159987e5972SCameron Grant 	"20 bit ADC"
160987e5972SCameron Grant };
161987e5972SCameron Grant 
16239004e69SCameron Grant static char *ac97extfeature[] = {
16339004e69SCameron Grant 	"variable rate PCM",
16439004e69SCameron Grant 	"double rate PCM",
16539004e69SCameron Grant 	"reserved 1",
16639004e69SCameron Grant 	"variable rate mic",
16739004e69SCameron Grant 	"reserved 2",
16839004e69SCameron Grant 	"reserved 3",
16939004e69SCameron Grant 	"center DAC",
17039004e69SCameron Grant 	"surround DAC",
17139004e69SCameron Grant 	"LFE DAC",
17239004e69SCameron Grant 	"AMAP",
17339004e69SCameron Grant 	"reserved 4",
17439004e69SCameron Grant 	"reserved 5",
17539004e69SCameron Grant 	"reserved 6",
17639004e69SCameron Grant 	"reserved 7",
17739004e69SCameron Grant };
17839004e69SCameron Grant 
17939004e69SCameron Grant static u_int16_t
18039004e69SCameron Grant rdcd(struct ac97_info *codec, int reg)
18139004e69SCameron Grant {
1820f55ac6cSCameron Grant 	return AC97_READ(codec->methods, codec->devinfo, reg);
18339004e69SCameron Grant }
18439004e69SCameron Grant 
18539004e69SCameron Grant static void
18639004e69SCameron Grant wrcd(struct ac97_info *codec, int reg, u_int16_t val)
18739004e69SCameron Grant {
1880f55ac6cSCameron Grant 	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
18939004e69SCameron Grant }
19039004e69SCameron Grant 
19139004e69SCameron Grant int
19239004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate)
19339004e69SCameron Grant {
19439004e69SCameron Grant 	u_int16_t v;
19539004e69SCameron Grant 
19639004e69SCameron Grant 	switch(which) {
19739004e69SCameron Grant 	case AC97_REGEXT_FDACRATE:
19839004e69SCameron Grant 	case AC97_REGEXT_SDACRATE:
19939004e69SCameron Grant 	case AC97_REGEXT_LDACRATE:
20039004e69SCameron Grant 	case AC97_REGEXT_LADCRATE:
20139004e69SCameron Grant 	case AC97_REGEXT_MADCRATE:
20239004e69SCameron Grant 		break;
20339004e69SCameron Grant 
20439004e69SCameron Grant 	default:
20539004e69SCameron Grant 		return -1;
20639004e69SCameron Grant 	}
20739004e69SCameron Grant 
20866ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
20939004e69SCameron Grant 	if (rate != 0) {
21039004e69SCameron Grant 		v = rate;
21139004e69SCameron Grant 		if (codec->extstat & AC97_EXTCAP_DRA)
21239004e69SCameron Grant 			v >>= 1;
21339004e69SCameron Grant 		wrcd(codec, which, v);
21439004e69SCameron Grant 	}
21539004e69SCameron Grant 	v = rdcd(codec, which);
21639004e69SCameron Grant 	if (codec->extstat & AC97_EXTCAP_DRA)
21739004e69SCameron Grant 		v <<= 1;
21866ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
21939004e69SCameron Grant 	return v;
22039004e69SCameron Grant }
22139004e69SCameron Grant 
22239004e69SCameron Grant int
22339004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
22439004e69SCameron Grant {
22539004e69SCameron Grant 	mode &= AC97_EXTCAPS;
22639004e69SCameron Grant 	if ((mode & ~codec->extcaps) != 0)
22739004e69SCameron Grant 		return -1;
22866ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
22939004e69SCameron Grant 	wrcd(codec, AC97_REGEXT_STAT, mode);
23039004e69SCameron Grant 	codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
23166ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
23239004e69SCameron Grant 	return (mode == codec->extstat)? 0 : -1;
23339004e69SCameron Grant }
23439004e69SCameron Grant 
2359ec437a3SCameron Grant u_int16_t
2369ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec)
2379ec437a3SCameron Grant {
2389ec437a3SCameron Grant 	return codec->extstat;
2399ec437a3SCameron Grant }
2409ec437a3SCameron Grant 
2419ec437a3SCameron Grant u_int16_t
2429ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec)
2439ec437a3SCameron Grant {
2449ec437a3SCameron Grant 	return codec->extcaps;
2459ec437a3SCameron Grant }
2469ec437a3SCameron Grant 
247987e5972SCameron Grant static int
248987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel)
249987e5972SCameron Grant {
250987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
251341f16ccSCameron Grant 
252987e5972SCameron Grant 	if (e->recidx > 0) {
253987e5972SCameron Grant 		int val = e->recidx - 1;
254987e5972SCameron Grant 		val |= val << 8;
25566ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
25639004e69SCameron Grant 		wrcd(codec, AC97_REG_RECSEL, val);
25766ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
258987e5972SCameron Grant 		return 0;
25939004e69SCameron Grant 	} else
26039004e69SCameron Grant 		return -1;
261987e5972SCameron Grant }
262987e5972SCameron Grant 
263987e5972SCameron Grant static int
264987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
265987e5972SCameron Grant {
266987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
267341f16ccSCameron Grant 
268341f16ccSCameron Grant 	if (e->reg && e->enable && e->bits) {
269e479a8afSCameron Grant 		int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
270987e5972SCameron Grant 
27139004e69SCameron Grant 		if (!e->stereo)
27239004e69SCameron Grant 			right = left;
273987e5972SCameron Grant 		if (e->reg > 0) {
274987e5972SCameron Grant 			left = 100 - left;
275987e5972SCameron Grant 			right = 100 - right;
276987e5972SCameron Grant 		}
277987e5972SCameron Grant 
278987e5972SCameron Grant 		max = (1 << e->bits) - 1;
279987e5972SCameron Grant 		left = (left * max) / 100;
280987e5972SCameron Grant 		right = (right * max) / 100;
281987e5972SCameron Grant 
282987e5972SCameron Grant 		val = (left << 8) | right;
283987e5972SCameron Grant 
284987e5972SCameron Grant 		left = (left * 100) / max;
285987e5972SCameron Grant 		right = (right * 100) / max;
286987e5972SCameron Grant 
287987e5972SCameron Grant 		if (e->reg > 0) {
288987e5972SCameron Grant 			left = 100 - left;
289987e5972SCameron Grant 			right = 100 - right;
290987e5972SCameron Grant 		}
291987e5972SCameron Grant 
292987e5972SCameron Grant 		if (!e->stereo) {
293987e5972SCameron Grant 			val &= max;
294987e5972SCameron Grant 			val <<= e->ofs;
295987e5972SCameron Grant 			if (e->mask) {
29639004e69SCameron Grant 				int cur = rdcd(codec, e->reg);
297987e5972SCameron Grant 				val |= cur & ~(max << e->ofs);
298987e5972SCameron Grant 			}
299987e5972SCameron Grant 		}
30039004e69SCameron Grant 		if (left == 0 && right == 0 && e->mute == 1)
30139004e69SCameron Grant 			val = AC97_MUTE;
30266ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
30339004e69SCameron Grant 		wrcd(codec, reg, val);
30466ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
305987e5972SCameron Grant 		return left | (right << 8);
306341f16ccSCameron Grant 	} else {
307341f16ccSCameron Grant 		/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
30839004e69SCameron Grant 		return -1;
309987e5972SCameron Grant 	}
310341f16ccSCameron Grant }
311987e5972SCameron Grant 
312987e5972SCameron Grant #if 0
313987e5972SCameron Grant static int
314987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel)
315987e5972SCameron Grant {
316987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
317987e5972SCameron Grant 	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
318987e5972SCameron Grant 		int max, val, volume;
319987e5972SCameron Grant 
320987e5972SCameron Grant 		max = (1 << e->bits) - 1;
32139004e69SCameron Grant 		val = rdcd(code, e->reg);
32239004e69SCameron Grant 		if (val == AC97_MUTE && e->mute == 1)
32339004e69SCameron Grant 			volume = 0;
324987e5972SCameron Grant 		else {
325987e5972SCameron Grant 			if (e->stereo == 0) val >>= e->ofs;
326987e5972SCameron Grant 			val &= max;
327987e5972SCameron Grant 			volume = (val * 100) / max;
328987e5972SCameron Grant 			if (e->reg > 0) volume = 100 - volume;
329987e5972SCameron Grant 		}
330987e5972SCameron Grant 		return volume;
33139004e69SCameron Grant 	} else
33239004e69SCameron Grant 		return -1;
333987e5972SCameron Grant }
334987e5972SCameron Grant #endif
335987e5972SCameron Grant 
336987e5972SCameron Grant static unsigned
33739004e69SCameron Grant ac97_initmixer(struct ac97_info *codec)
338987e5972SCameron Grant {
339341f16ccSCameron Grant 	unsigned i, j, k, old;
340987e5972SCameron Grant 	u_int32_t id;
341987e5972SCameron Grant 
34266ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
34339004e69SCameron Grant 	for (i = 0; i < 32; i++)
34439004e69SCameron Grant 		codec->mix[i] = ac97mixtable_default[i];
345987e5972SCameron Grant 
3460f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
347cd2c103aSCameron Grant 	if (codec->count == 0) {
34804553e63SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
34966ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
35004553e63SCameron Grant 		return ENODEV;
35104553e63SCameron Grant 	}
3529ec437a3SCameron Grant 
35371508289SCameron Grant 	wrcd(codec, AC97_REG_POWER, 0x8000);
35439004e69SCameron Grant 	wrcd(codec, AC97_REG_RESET, 0);
355e620d959SCameron Grant 	DELAY(100000);
35671508289SCameron Grant 	wrcd(codec, AC97_REG_POWER, 0x8000);
357987e5972SCameron Grant 
35839004e69SCameron Grant 	i = rdcd(codec, AC97_REG_RESET);
359987e5972SCameron Grant 	codec->caps = i & 0x03ff;
360987e5972SCameron Grant 	codec->se =  (i & 0x7c00) >> 10;
361987e5972SCameron Grant 
3626b4b88f7SCameron Grant 	id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2);
3636b4b88f7SCameron Grant 	codec->rev = id & 0x000000ff;
364e620d959SCameron Grant 	if (id == 0 || id == 0xffffffff) {
365e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
36666ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
367e620d959SCameron Grant 		return ENODEV;
368e620d959SCameron Grant 	}
3696b4b88f7SCameron Grant 
370cd2c103aSCameron Grant 	codec->noext = 0;
37166ef8af5SCameron Grant 	codec->id = NULL;
372cd2c103aSCameron Grant 	for (i = 0; ac97codecid[i].id; i++) {
373cd2c103aSCameron Grant 		if (ac97codecid[i].id == id) {
37466ef8af5SCameron Grant 			codec->id = ac97codecid[i].name;
375cd2c103aSCameron Grant 			codec->noext = ac97codecid[i].noext;
376cd2c103aSCameron Grant 		}
377cd2c103aSCameron Grant 	}
3786b4b88f7SCameron Grant 
379cd2c103aSCameron Grant 	codec->extcaps = 0;
380cd2c103aSCameron Grant 	codec->extid = 0;
381cd2c103aSCameron Grant 	codec->extstat = 0;
3826a6ee5bbSCameron Grant 	if (!codec->noext) {
38339004e69SCameron Grant 		i = rdcd(codec, AC97_REGEXT_ID);
3846a6ee5bbSCameron Grant 		if (i != 0xffff) {
38539004e69SCameron Grant 			codec->extcaps = i & 0x3fff;
38639004e69SCameron Grant 			codec->extid =  (i & 0xc000) >> 14;
38739004e69SCameron Grant 			codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
3886b4b88f7SCameron Grant 		}
3896a6ee5bbSCameron Grant 	}
390987e5972SCameron Grant 
391341f16ccSCameron Grant 	for (i = 0; i < 32; i++) {
39233c878f0SCameron Grant 		k = codec->noext? codec->mix[i].enable : 1;
39333c878f0SCameron Grant 		if (k && (codec->mix[i].reg > 0)) {
394341f16ccSCameron Grant 			old = rdcd(codec, codec->mix[i].reg);
395341f16ccSCameron Grant 			wrcd(codec, codec->mix[i].reg, 0x3f);
396341f16ccSCameron Grant 			j = rdcd(codec, codec->mix[i].reg);
397341f16ccSCameron Grant 			wrcd(codec, codec->mix[i].reg, old);
3986a6ee5bbSCameron Grant 			codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
399341f16ccSCameron Grant 			for (k = 1; j & (1 << k); k++);
400341f16ccSCameron Grant 			codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
401341f16ccSCameron Grant 		}
402341f16ccSCameron Grant 		/* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
403341f16ccSCameron Grant 	}
404987e5972SCameron Grant 
405987e5972SCameron Grant 	if (bootverbose) {
406e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec id 0x%08x", id);
40766ef8af5SCameron Grant 		if (codec->id)
40866ef8af5SCameron Grant 			printf(" (%s)", codec->id);
40903a00905SCameron Grant 		printf("\n");
41003a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec features ");
41139004e69SCameron Grant 		for (i = j = 0; i < 10; i++)
41239004e69SCameron Grant 			if (codec->caps & (1 << i))
41339004e69SCameron Grant 				printf("%s%s", j++? ", " : "", ac97feature[i]);
41439004e69SCameron Grant 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
415987e5972SCameron Grant 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
41639004e69SCameron Grant 
41739004e69SCameron Grant 		if (codec->extcaps != 0 || codec->extid) {
41839004e69SCameron Grant 			device_printf(codec->dev, "ac97 %s codec",
41939004e69SCameron Grant 				      codec->extid? "secondary" : "primary");
42039004e69SCameron Grant 			if (codec->extcaps)
42139004e69SCameron Grant 				printf(" extended features ");
42239004e69SCameron Grant 			for (i = j = 0; i < 14; i++)
42339004e69SCameron Grant 				if (codec->extcaps & (1 << i))
42439004e69SCameron Grant 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
42539004e69SCameron Grant 			printf("\n");
42639004e69SCameron Grant 		}
427987e5972SCameron Grant 	}
428987e5972SCameron Grant 
42939004e69SCameron Grant 	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
43003a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
43166ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
432987e5972SCameron Grant 	return 0;
433987e5972SCameron Grant }
434987e5972SCameron Grant 
4359ec437a3SCameron Grant static unsigned
4369ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec)
4379ec437a3SCameron Grant {
4389ec437a3SCameron Grant 	unsigned i;
4399ec437a3SCameron Grant 
44066ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
4410f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
4429ec437a3SCameron Grant 	if (codec->count == 0) {
4439ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
44466ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
4459ec437a3SCameron Grant 		return ENODEV;
4469ec437a3SCameron Grant 	}
4479ec437a3SCameron Grant 
4489ec437a3SCameron Grant 	wrcd(codec, AC97_REG_POWER, 0);
4499ec437a3SCameron Grant 	wrcd(codec, AC97_REG_RESET, 0);
4509ec437a3SCameron Grant 	DELAY(100000);
4519ec437a3SCameron Grant 	i = rdcd(codec, AC97_REG_RESET);
4529ec437a3SCameron Grant 
4539ec437a3SCameron Grant 	if (!codec->noext) {
4549ec437a3SCameron Grant 		wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
4559ec437a3SCameron Grant 		if (rdcd(codec, AC97_REGEXT_STAT) != codec->extstat)
4569ec437a3SCameron Grant 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
4579ec437a3SCameron Grant 				      codec->extstat, rdcd(codec, AC97_REGEXT_STAT));
4589ec437a3SCameron Grant 	}
4599ec437a3SCameron Grant 
4609ec437a3SCameron Grant 	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
4619ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
46266ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
4639ec437a3SCameron Grant 	return 0;
4649ec437a3SCameron Grant }
4659ec437a3SCameron Grant 
466987e5972SCameron Grant struct ac97_info *
4670f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
468987e5972SCameron Grant {
469987e5972SCameron Grant 	struct ac97_info *codec;
470987e5972SCameron Grant 
4710f55ac6cSCameron Grant 	codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
4720f55ac6cSCameron Grant 	if (codec == NULL)
4730f55ac6cSCameron Grant 		return NULL;
4740f55ac6cSCameron Grant 
47566ef8af5SCameron Grant 	snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
47666ef8af5SCameron Grant 	codec->lock = snd_mtxcreate(codec->name);
4770f55ac6cSCameron Grant 	codec->methods = kobj_create(cls, M_AC97, M_WAITOK);
4780f55ac6cSCameron Grant 	if (codec->methods == NULL) {
47966ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
48066ef8af5SCameron Grant 		snd_mtxfree(codec->lock);
4810f55ac6cSCameron Grant 		free(codec, M_AC97);
4820f55ac6cSCameron Grant 		return NULL;
483987e5972SCameron Grant 	}
4840f55ac6cSCameron Grant 
4850f55ac6cSCameron Grant 	codec->dev = dev;
4860f55ac6cSCameron Grant 	codec->devinfo = devinfo;
487987e5972SCameron Grant 	return codec;
488987e5972SCameron Grant }
489987e5972SCameron Grant 
49033dbf14aSCameron Grant void
49133dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec)
49233dbf14aSCameron Grant {
49366ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
4940f55ac6cSCameron Grant 	if (codec->methods != NULL)
4950f55ac6cSCameron Grant 		kobj_delete(codec->methods, M_AC97);
49666ef8af5SCameron Grant 	snd_mtxfree(codec->lock);
4970f55ac6cSCameron Grant 	free(codec, M_AC97);
49833dbf14aSCameron Grant }
49933dbf14aSCameron Grant 
5000f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
5010f55ac6cSCameron Grant 
502987e5972SCameron Grant static int
50366ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m)
504987e5972SCameron Grant {
505987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
506341f16ccSCameron Grant 	u_int32_t i, mask;
507341f16ccSCameron Grant 
50839004e69SCameron Grant 	if (codec == NULL)
50939004e69SCameron Grant 		return -1;
510341f16ccSCameron Grant 
511e620d959SCameron Grant 	if (ac97_initmixer(codec))
512e620d959SCameron Grant 		return -1;
513341f16ccSCameron Grant 
514341f16ccSCameron Grant 	mask = 0;
515341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
516341f16ccSCameron Grant 		mask |= codec->mix[i].enable? 1 << i : 0;
517341f16ccSCameron Grant 	mix_setdevs(m, mask);
518341f16ccSCameron Grant 
519341f16ccSCameron Grant 	mask = 0;
520341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
521341f16ccSCameron Grant 		mask |= codec->mix[i].recidx? 1 << i : 0;
522341f16ccSCameron Grant 	mix_setrecdevs(m, mask);
523987e5972SCameron Grant 	return 0;
524987e5972SCameron Grant }
525987e5972SCameron Grant 
526987e5972SCameron Grant static int
52766ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m)
52833dbf14aSCameron Grant {
52933dbf14aSCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
530341f16ccSCameron Grant 
53133dbf14aSCameron Grant 	if (codec == NULL)
53233dbf14aSCameron Grant 		return -1;
53333dbf14aSCameron Grant 	/*
53433dbf14aSCameron Grant 	if (ac97_uninitmixer(codec))
53533dbf14aSCameron Grant 		return -1;
53633dbf14aSCameron Grant 	*/
53733dbf14aSCameron Grant 	ac97_destroy(codec);
53833dbf14aSCameron Grant 	return 0;
53933dbf14aSCameron Grant }
54033dbf14aSCameron Grant 
54133dbf14aSCameron Grant static int
54266ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m)
5439ec437a3SCameron Grant {
5449ec437a3SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
5459ec437a3SCameron Grant 
5469ec437a3SCameron Grant 	if (codec == NULL)
5479ec437a3SCameron Grant 		return -1;
5489ec437a3SCameron Grant 	return ac97_reinitmixer(codec);
5499ec437a3SCameron Grant }
5509ec437a3SCameron Grant 
5519ec437a3SCameron Grant static int
55266ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
553987e5972SCameron Grant {
554987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
555341f16ccSCameron Grant 
55639004e69SCameron Grant 	if (codec == NULL)
55739004e69SCameron Grant 		return -1;
558987e5972SCameron Grant 	return ac97_setmixer(codec, dev, left, right);
559987e5972SCameron Grant }
560987e5972SCameron Grant 
561987e5972SCameron Grant static int
56266ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
563987e5972SCameron Grant {
564987e5972SCameron Grant 	int i;
565987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
566341f16ccSCameron Grant 
56739004e69SCameron Grant 	if (codec == NULL)
56839004e69SCameron Grant 		return -1;
569987e5972SCameron Grant 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
57039004e69SCameron Grant 		if ((src & (1 << i)) != 0)
57139004e69SCameron Grant 			break;
572987e5972SCameron Grant 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
573987e5972SCameron Grant }
574987e5972SCameron Grant 
5750f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = {
5760f55ac6cSCameron Grant     	KOBJMETHOD(mixer_init,		ac97mix_init),
5770f55ac6cSCameron Grant     	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
5780f55ac6cSCameron Grant     	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
5790f55ac6cSCameron Grant     	KOBJMETHOD(mixer_set,		ac97mix_set),
5800f55ac6cSCameron Grant     	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
5810f55ac6cSCameron Grant 	{ 0, 0 }
582987e5972SCameron Grant };
5830f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer);
5840f55ac6cSCameron Grant 
5850f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
5860f55ac6cSCameron Grant 
5870f55ac6cSCameron Grant kobj_class_t
5880f55ac6cSCameron Grant ac97_getmixerclass(void)
5890f55ac6cSCameron Grant {
5900f55ac6cSCameron Grant 	return &ac97mixer_class;
5910f55ac6cSCameron Grant }
5920f55ac6cSCameron Grant 
593987e5972SCameron Grant 
594