xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision 9ec437a334552380e562fb8a68387459c89f038d)
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 
32987e5972SCameron Grant struct ac97mixtable_entry {
33987e5972SCameron Grant 	int		reg:8;
34987e5972SCameron Grant 	unsigned	bits:4;
35987e5972SCameron Grant 	unsigned	ofs:4;
36987e5972SCameron Grant 	unsigned	stereo:1;
37987e5972SCameron Grant 	unsigned	mute:1;
38987e5972SCameron Grant 	unsigned	recidx:4;
39987e5972SCameron Grant 	unsigned        mask:1;
40341f16ccSCameron Grant 	unsigned	enable:1;
41987e5972SCameron Grant };
42987e5972SCameron Grant 
43987e5972SCameron Grant struct ac97_info {
4403a00905SCameron Grant 	device_t dev;
4539004e69SCameron Grant 	ac97_init *init;
46987e5972SCameron Grant 	ac97_read *read;
47987e5972SCameron Grant 	ac97_write *write;
48987e5972SCameron Grant 	void *devinfo;
49cd2c103aSCameron Grant 	char *name;
50987e5972SCameron Grant 	char rev;
51cd2c103aSCameron Grant 	unsigned count, caps, se, extcaps, extid, extstat, noext:1;
52987e5972SCameron Grant 	struct ac97mixtable_entry mix[32];
53987e5972SCameron Grant };
54987e5972SCameron Grant 
55987e5972SCameron Grant struct ac97_codecid {
566b4b88f7SCameron Grant 	u_int32_t id, noext:1;
57987e5972SCameron Grant 	char *name;
58987e5972SCameron Grant };
59987e5972SCameron Grant 
60987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = {
61341f16ccSCameron Grant 	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0, 1 },
62341f16ccSCameron Grant 	[SOUND_MIXER_MONITOR]	= { AC97_MIX_PHONES, 	5, 0, 1, 1, 0, 0, 0 },
63341f16ccSCameron Grant 	[SOUND_MIXER_PHONEOUT]	= { AC97_MIX_MONO, 	5, 0, 0, 1, 7, 0, 0 },
64341f16ccSCameron Grant 	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1, 0 },
65341f16ccSCameron Grant 	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1, 0 },
66341f16ccSCameron Grant 	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0, 1 },
67341f16ccSCameron Grant 	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0, 0 },
68341f16ccSCameron Grant 	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0, 1 },
69341f16ccSCameron Grant 	[SOUND_MIXER_PHONEIN]	= { AC97_MIX_PHONE, 	5, 0, 0, 1, 8, 0, 0 },
70341f16ccSCameron Grant 	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 0, 1 },
71341f16ccSCameron Grant 	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
72341f16ccSCameron Grant 	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
73341f16ccSCameron Grant 	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
74341f16ccSCameron Grant 	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
75987e5972SCameron Grant };
76987e5972SCameron Grant 
77987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = {
786b4b88f7SCameron Grant 	{ 0x414b4d00, 1, "Asahi Kasei AK4540 rev 0" },
79cd2c103aSCameron Grant 	{ 0x414b4d01, 1, "Asahi Kasei AK4540 rev 1" },
806b4b88f7SCameron Grant 	{ 0x43525900, 0, "Cirrus Logic CS4297" 	},
816b4b88f7SCameron Grant 	{ 0x83847600, 0, "SigmaTel STAC????" 	},
826b4b88f7SCameron Grant 	{ 0x83847604, 0, "SigmaTel STAC9701/3/4/5" },
836b4b88f7SCameron Grant 	{ 0x83847605, 0, "SigmaTel STAC9704" 	},
846b4b88f7SCameron Grant 	{ 0x83847608, 0, "SigmaTel STAC9708" 	},
856b4b88f7SCameron Grant 	{ 0x83847609, 0, "SigmaTel STAC9721" 	},
866b4b88f7SCameron Grant 	{ 0, 	      0, NULL			}
87987e5972SCameron Grant };
88987e5972SCameron Grant 
89987e5972SCameron Grant static char *ac97enhancement[] = {
9004553e63SCameron Grant 	"no 3D Stereo Enhancement",
91987e5972SCameron Grant 	"Analog Devices Phat Stereo",
92987e5972SCameron Grant 	"Creative Stereo Enhancement",
93987e5972SCameron Grant 	"National Semi 3D Stereo Enhancement",
94987e5972SCameron Grant 	"Yamaha Ymersion",
95987e5972SCameron Grant 	"BBE 3D Stereo Enhancement",
96987e5972SCameron Grant 	"Crystal Semi 3D Stereo Enhancement",
97987e5972SCameron Grant 	"Qsound QXpander",
98987e5972SCameron Grant 	"Spatializer 3D Stereo Enhancement",
99987e5972SCameron Grant 	"SRS 3D Stereo Enhancement",
100987e5972SCameron Grant 	"Platform Tech 3D Stereo Enhancement",
101987e5972SCameron Grant 	"AKM 3D Audio",
102987e5972SCameron Grant 	"Aureal Stereo Enhancement",
103987e5972SCameron Grant 	"Aztech 3D Enhancement",
104987e5972SCameron Grant 	"Binaura 3D Audio Enhancement",
105987e5972SCameron Grant 	"ESS Technology Stereo Enhancement",
106987e5972SCameron Grant 	"Harman International VMAx",
107987e5972SCameron Grant 	"Nvidea 3D Stereo Enhancement",
108987e5972SCameron Grant 	"Philips Incredible Sound",
109987e5972SCameron Grant 	"Texas Instruments 3D Stereo Enhancement",
110987e5972SCameron Grant 	"VLSI Technology 3D Stereo Enhancement",
111987e5972SCameron Grant 	"TriTech 3D Stereo Enhancement",
112987e5972SCameron Grant 	"Realtek 3D Stereo Enhancement",
113987e5972SCameron Grant 	"Samsung 3D Stereo Enhancement",
114987e5972SCameron Grant 	"Wolfson Microelectronics 3D Enhancement",
115987e5972SCameron Grant 	"Delta Integration 3D Enhancement",
116987e5972SCameron Grant 	"SigmaTel 3D Enhancement",
117987e5972SCameron Grant 	"Reserved 27",
118987e5972SCameron Grant 	"Rockwell 3D Stereo Enhancement",
119987e5972SCameron Grant 	"Reserved 29",
120987e5972SCameron Grant 	"Reserved 30",
121987e5972SCameron Grant 	"Reserved 31"
122987e5972SCameron Grant };
123987e5972SCameron Grant 
124987e5972SCameron Grant static char *ac97feature[] = {
125987e5972SCameron Grant 	"mic channel",
126987e5972SCameron Grant 	"reserved",
127987e5972SCameron Grant 	"tone",
128987e5972SCameron Grant 	"simulated stereo",
129987e5972SCameron Grant 	"headphone",
130987e5972SCameron Grant 	"bass boost",
131987e5972SCameron Grant 	"18 bit DAC",
132987e5972SCameron Grant 	"20 bit DAC",
133987e5972SCameron Grant 	"18 bit ADC",
134987e5972SCameron Grant 	"20 bit ADC"
135987e5972SCameron Grant };
136987e5972SCameron Grant 
13739004e69SCameron Grant static char *ac97extfeature[] = {
13839004e69SCameron Grant 	"variable rate PCM",
13939004e69SCameron Grant 	"double rate PCM",
14039004e69SCameron Grant 	"reserved 1",
14139004e69SCameron Grant 	"variable rate mic",
14239004e69SCameron Grant 	"reserved 2",
14339004e69SCameron Grant 	"reserved 3",
14439004e69SCameron Grant 	"center DAC",
14539004e69SCameron Grant 	"surround DAC",
14639004e69SCameron Grant 	"LFE DAC",
14739004e69SCameron Grant 	"AMAP",
14839004e69SCameron Grant 	"reserved 4",
14939004e69SCameron Grant 	"reserved 5",
15039004e69SCameron Grant 	"reserved 6",
15139004e69SCameron Grant 	"reserved 7",
15239004e69SCameron Grant };
15339004e69SCameron Grant 
15439004e69SCameron Grant static u_int16_t
15539004e69SCameron Grant rdcd(struct ac97_info *codec, int reg)
15639004e69SCameron Grant {
15739004e69SCameron Grant 	return codec->read(codec->devinfo, reg);
15839004e69SCameron Grant }
15939004e69SCameron Grant 
16039004e69SCameron Grant static void
16139004e69SCameron Grant wrcd(struct ac97_info *codec, int reg, u_int16_t val)
16239004e69SCameron Grant {
16339004e69SCameron Grant 	codec->write(codec->devinfo, reg, val);
16439004e69SCameron Grant }
16539004e69SCameron Grant 
16639004e69SCameron Grant int
16739004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate)
16839004e69SCameron Grant {
16939004e69SCameron Grant 	u_int16_t v;
17039004e69SCameron Grant 
17139004e69SCameron Grant 	switch(which) {
17239004e69SCameron Grant 	case AC97_REGEXT_FDACRATE:
17339004e69SCameron Grant 	case AC97_REGEXT_SDACRATE:
17439004e69SCameron Grant 	case AC97_REGEXT_LDACRATE:
17539004e69SCameron Grant 	case AC97_REGEXT_LADCRATE:
17639004e69SCameron Grant 	case AC97_REGEXT_MADCRATE:
17739004e69SCameron Grant 		break;
17839004e69SCameron Grant 
17939004e69SCameron Grant 	default:
18039004e69SCameron Grant 		return -1;
18139004e69SCameron Grant 	}
18239004e69SCameron Grant 
18339004e69SCameron Grant 	if (rate != 0) {
18439004e69SCameron Grant 		v = rate;
18539004e69SCameron Grant 		if (codec->extstat & AC97_EXTCAP_DRA)
18639004e69SCameron Grant 			v >>= 1;
18739004e69SCameron Grant 		wrcd(codec, which, v);
18839004e69SCameron Grant 	}
18939004e69SCameron Grant 	v = rdcd(codec, which);
19039004e69SCameron Grant 	if (codec->extstat & AC97_EXTCAP_DRA)
19139004e69SCameron Grant 		v <<= 1;
19239004e69SCameron Grant 	return v;
19339004e69SCameron Grant }
19439004e69SCameron Grant 
19539004e69SCameron Grant int
19639004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
19739004e69SCameron Grant {
19839004e69SCameron Grant 	mode &= AC97_EXTCAPS;
19939004e69SCameron Grant 	if ((mode & ~codec->extcaps) != 0)
20039004e69SCameron Grant 		return -1;
20139004e69SCameron Grant 	wrcd(codec, AC97_REGEXT_STAT, mode);
20239004e69SCameron Grant 	codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
20339004e69SCameron Grant 	return (mode == codec->extstat)? 0 : -1;
20439004e69SCameron Grant }
20539004e69SCameron Grant 
2069ec437a3SCameron Grant u_int16_t
2079ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec)
2089ec437a3SCameron Grant {
2099ec437a3SCameron Grant 	return codec->extstat;
2109ec437a3SCameron Grant }
2119ec437a3SCameron Grant 
2129ec437a3SCameron Grant u_int16_t
2139ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec)
2149ec437a3SCameron Grant {
2159ec437a3SCameron Grant 	return codec->extcaps;
2169ec437a3SCameron Grant }
2179ec437a3SCameron Grant 
218987e5972SCameron Grant static int
219987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel)
220987e5972SCameron Grant {
221987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
222341f16ccSCameron Grant 
223987e5972SCameron Grant 	if (e->recidx > 0) {
224987e5972SCameron Grant 		int val = e->recidx - 1;
225987e5972SCameron Grant 		val |= val << 8;
22639004e69SCameron Grant 		wrcd(codec, AC97_REG_RECSEL, val);
227987e5972SCameron Grant 		return 0;
22839004e69SCameron Grant 	} else
22939004e69SCameron Grant 		return -1;
230987e5972SCameron Grant }
231987e5972SCameron Grant 
232987e5972SCameron Grant static int
233987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
234987e5972SCameron Grant {
235987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
236341f16ccSCameron Grant 
237341f16ccSCameron Grant 	if (e->reg && e->enable && e->bits) {
238e479a8afSCameron Grant 		int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
239987e5972SCameron Grant 
24039004e69SCameron Grant 		if (!e->stereo)
24139004e69SCameron Grant 			right = left;
242987e5972SCameron Grant 		if (e->reg > 0) {
243987e5972SCameron Grant 			left = 100 - left;
244987e5972SCameron Grant 			right = 100 - right;
245987e5972SCameron Grant 		}
246987e5972SCameron Grant 
247987e5972SCameron Grant 		max = (1 << e->bits) - 1;
248987e5972SCameron Grant 		left = (left * max) / 100;
249987e5972SCameron Grant 		right = (right * max) / 100;
250987e5972SCameron Grant 
251987e5972SCameron Grant 		val = (left << 8) | right;
252987e5972SCameron Grant 
253987e5972SCameron Grant 		left = (left * 100) / max;
254987e5972SCameron Grant 		right = (right * 100) / max;
255987e5972SCameron Grant 
256987e5972SCameron Grant 		if (e->reg > 0) {
257987e5972SCameron Grant 			left = 100 - left;
258987e5972SCameron Grant 			right = 100 - right;
259987e5972SCameron Grant 		}
260987e5972SCameron Grant 
261987e5972SCameron Grant 		if (!e->stereo) {
262987e5972SCameron Grant 			val &= max;
263987e5972SCameron Grant 			val <<= e->ofs;
264987e5972SCameron Grant 			if (e->mask) {
26539004e69SCameron Grant 				int cur = rdcd(codec, e->reg);
266987e5972SCameron Grant 				val |= cur & ~(max << e->ofs);
267987e5972SCameron Grant 			}
268987e5972SCameron Grant 		}
26939004e69SCameron Grant 		if (left == 0 && right == 0 && e->mute == 1)
27039004e69SCameron Grant 			val = AC97_MUTE;
27139004e69SCameron Grant 		wrcd(codec, reg, val);
272987e5972SCameron Grant 		return left | (right << 8);
273341f16ccSCameron Grant 	} else {
274341f16ccSCameron Grant 		/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
27539004e69SCameron Grant 		return -1;
276987e5972SCameron Grant 	}
277341f16ccSCameron Grant }
278987e5972SCameron Grant 
279987e5972SCameron Grant #if 0
280987e5972SCameron Grant static int
281987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel)
282987e5972SCameron Grant {
283987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
284987e5972SCameron Grant 	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
285987e5972SCameron Grant 		int max, val, volume;
286987e5972SCameron Grant 
287987e5972SCameron Grant 		max = (1 << e->bits) - 1;
28839004e69SCameron Grant 		val = rdcd(code, e->reg);
28939004e69SCameron Grant 		if (val == AC97_MUTE && e->mute == 1)
29039004e69SCameron Grant 			volume = 0;
291987e5972SCameron Grant 		else {
292987e5972SCameron Grant 			if (e->stereo == 0) val >>= e->ofs;
293987e5972SCameron Grant 			val &= max;
294987e5972SCameron Grant 			volume = (val * 100) / max;
295987e5972SCameron Grant 			if (e->reg > 0) volume = 100 - volume;
296987e5972SCameron Grant 		}
297987e5972SCameron Grant 		return volume;
29839004e69SCameron Grant 	} else
29939004e69SCameron Grant 		return -1;
300987e5972SCameron Grant }
301987e5972SCameron Grant #endif
302987e5972SCameron Grant 
303987e5972SCameron Grant static unsigned
30439004e69SCameron Grant ac97_initmixer(struct ac97_info *codec)
305987e5972SCameron Grant {
306341f16ccSCameron Grant 	unsigned i, j, k, old;
307987e5972SCameron Grant 	u_int32_t id;
308987e5972SCameron Grant 
30939004e69SCameron Grant 	for (i = 0; i < 32; i++)
31039004e69SCameron Grant 		codec->mix[i] = ac97mixtable_default[i];
311987e5972SCameron Grant 
31204553e63SCameron Grant 	if (codec->init) {
313cd2c103aSCameron Grant 		codec->count = codec->init(codec->devinfo);
314cd2c103aSCameron Grant 		if (codec->count == 0) {
31504553e63SCameron Grant 			device_printf(codec->dev, "ac97 codec init failed\n");
31604553e63SCameron Grant 			return ENODEV;
31704553e63SCameron Grant 		}
318cd2c103aSCameron Grant 	} else
319cd2c103aSCameron Grant 		codec->count = 1;
3209ec437a3SCameron Grant 
32139004e69SCameron Grant 	wrcd(codec, AC97_REG_POWER, 0);
32239004e69SCameron Grant 	wrcd(codec, AC97_REG_RESET, 0);
323e620d959SCameron Grant 	DELAY(100000);
324987e5972SCameron Grant 
32539004e69SCameron Grant 	i = rdcd(codec, AC97_REG_RESET);
326987e5972SCameron Grant 	codec->caps = i & 0x03ff;
327987e5972SCameron Grant 	codec->se =  (i & 0x7c00) >> 10;
328987e5972SCameron Grant 
3296b4b88f7SCameron Grant 	id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2);
3306b4b88f7SCameron Grant 	codec->rev = id & 0x000000ff;
331e620d959SCameron Grant 	if (id == 0 || id == 0xffffffff) {
332e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
333e620d959SCameron Grant 		return ENODEV;
334e620d959SCameron Grant 	}
3356b4b88f7SCameron Grant 
336cd2c103aSCameron Grant 	codec->noext = 0;
337cd2c103aSCameron Grant 	codec->name = NULL;
338cd2c103aSCameron Grant 	for (i = 0; ac97codecid[i].id; i++) {
339cd2c103aSCameron Grant 		if (ac97codecid[i].id == id) {
340cd2c103aSCameron Grant 			codec->name = ac97codecid[i].name;
341cd2c103aSCameron Grant 			codec->noext = ac97codecid[i].noext;
342cd2c103aSCameron Grant 		}
343cd2c103aSCameron Grant 	}
3446b4b88f7SCameron Grant 
345cd2c103aSCameron Grant 	if (codec->noext) {
346cd2c103aSCameron Grant 		codec->extcaps = 0;
347cd2c103aSCameron Grant 		codec->extid = 0;
348cd2c103aSCameron Grant 		codec->extstat = 0;
349cd2c103aSCameron Grant 	} else {
35039004e69SCameron Grant 		i = rdcd(codec, AC97_REGEXT_ID);
35139004e69SCameron Grant 		codec->extcaps = i & 0x3fff;
35239004e69SCameron Grant 		codec->extid =  (i & 0xc000) >> 14;
35339004e69SCameron Grant 		codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
3546b4b88f7SCameron Grant 	}
355987e5972SCameron Grant 
356341f16ccSCameron Grant 	for (i = 0; i < 32; i++) {
35733c878f0SCameron Grant 		k = codec->noext? codec->mix[i].enable : 1;
35833c878f0SCameron Grant 		if (k && (codec->mix[i].reg > 0)) {
359341f16ccSCameron Grant 			old = rdcd(codec, codec->mix[i].reg);
360341f16ccSCameron Grant 			wrcd(codec, codec->mix[i].reg, 0x3f);
361341f16ccSCameron Grant 			j = rdcd(codec, codec->mix[i].reg);
362341f16ccSCameron Grant 			wrcd(codec, codec->mix[i].reg, old);
363341f16ccSCameron Grant 			codec->mix[i].enable = j? 1 : 0;
364341f16ccSCameron Grant 			for (k = 1; j & (1 << k); k++);
365341f16ccSCameron Grant 			codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
366341f16ccSCameron Grant 		}
367341f16ccSCameron Grant 		/* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
368341f16ccSCameron Grant 	}
369987e5972SCameron Grant 
370987e5972SCameron Grant 	if (bootverbose) {
371e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec id 0x%08x", id);
372cd2c103aSCameron Grant 		if (codec->name)
373cd2c103aSCameron Grant 			printf(" (%s)", codec->name);
37403a00905SCameron Grant 		printf("\n");
37503a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec features ");
37639004e69SCameron Grant 		for (i = j = 0; i < 10; i++)
37739004e69SCameron Grant 			if (codec->caps & (1 << i))
37839004e69SCameron Grant 				printf("%s%s", j++? ", " : "", ac97feature[i]);
37939004e69SCameron Grant 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
380987e5972SCameron Grant 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
38139004e69SCameron Grant 
38239004e69SCameron Grant 		if (codec->extcaps != 0 || codec->extid) {
38339004e69SCameron Grant 			device_printf(codec->dev, "ac97 %s codec",
38439004e69SCameron Grant 				      codec->extid? "secondary" : "primary");
38539004e69SCameron Grant 			if (codec->extcaps)
38639004e69SCameron Grant 				printf(" extended features ");
38739004e69SCameron Grant 			for (i = j = 0; i < 14; i++)
38839004e69SCameron Grant 				if (codec->extcaps & (1 << i))
38939004e69SCameron Grant 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
39039004e69SCameron Grant 			printf("\n");
39139004e69SCameron Grant 		}
392987e5972SCameron Grant 	}
393987e5972SCameron Grant 
39439004e69SCameron Grant 	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
39503a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
396987e5972SCameron Grant 	return 0;
397987e5972SCameron Grant }
398987e5972SCameron Grant 
3999ec437a3SCameron Grant static unsigned
4009ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec)
4019ec437a3SCameron Grant {
4029ec437a3SCameron Grant 	unsigned i;
4039ec437a3SCameron Grant 
4049ec437a3SCameron Grant 	if (codec->init) {
4059ec437a3SCameron Grant 		codec->count = codec->init(codec->devinfo);
4069ec437a3SCameron Grant 		if (codec->count == 0) {
4079ec437a3SCameron Grant 			device_printf(codec->dev, "ac97 codec init failed\n");
4089ec437a3SCameron Grant 			return ENODEV;
4099ec437a3SCameron Grant 		}
4109ec437a3SCameron Grant 	} else
4119ec437a3SCameron Grant 		codec->count = 1;
4129ec437a3SCameron Grant 
4139ec437a3SCameron Grant 	wrcd(codec, AC97_REG_POWER, 0);
4149ec437a3SCameron Grant 	wrcd(codec, AC97_REG_RESET, 0);
4159ec437a3SCameron Grant 	DELAY(100000);
4169ec437a3SCameron Grant 	i = rdcd(codec, AC97_REG_RESET);
4179ec437a3SCameron Grant 
4189ec437a3SCameron Grant 	if (!codec->noext) {
4199ec437a3SCameron Grant 		wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
4209ec437a3SCameron Grant 		if (rdcd(codec, AC97_REGEXT_STAT) != codec->extstat)
4219ec437a3SCameron Grant 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
4229ec437a3SCameron Grant 				      codec->extstat, rdcd(codec, AC97_REGEXT_STAT));
4239ec437a3SCameron Grant 	}
4249ec437a3SCameron Grant 
4259ec437a3SCameron Grant 	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
4269ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
4279ec437a3SCameron Grant 	return 0;
4289ec437a3SCameron Grant }
4299ec437a3SCameron Grant 
430987e5972SCameron Grant struct ac97_info *
43139004e69SCameron Grant ac97_create(device_t dev, void *devinfo, ac97_init *init, ac97_read *rd, ac97_write *wr)
432987e5972SCameron Grant {
433987e5972SCameron Grant 	struct ac97_info *codec;
434987e5972SCameron Grant 
435987e5972SCameron Grant 	codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT);
436987e5972SCameron Grant 	if (codec != NULL) {
43703a00905SCameron Grant 		codec->dev = dev;
43839004e69SCameron Grant 		codec->init = init;
439987e5972SCameron Grant 		codec->read = rd;
440987e5972SCameron Grant 		codec->write = wr;
441987e5972SCameron Grant 		codec->devinfo = devinfo;
442987e5972SCameron Grant 	}
443987e5972SCameron Grant 	return codec;
444987e5972SCameron Grant }
445987e5972SCameron Grant 
44633dbf14aSCameron Grant void
44733dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec)
44833dbf14aSCameron Grant {
44933dbf14aSCameron Grant 	free(codec, M_DEVBUF);
45033dbf14aSCameron Grant }
45133dbf14aSCameron Grant 
452987e5972SCameron Grant static int
453987e5972SCameron Grant ac97mix_init(snd_mixer *m)
454987e5972SCameron Grant {
455987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
456341f16ccSCameron Grant 	u_int32_t i, mask;
457341f16ccSCameron Grant 
45839004e69SCameron Grant 	if (codec == NULL)
45939004e69SCameron Grant 		return -1;
460341f16ccSCameron Grant 
461e620d959SCameron Grant 	if (ac97_initmixer(codec))
462e620d959SCameron Grant 		return -1;
463341f16ccSCameron Grant 
464341f16ccSCameron Grant 	mask = 0;
465341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
466341f16ccSCameron Grant 		mask |= codec->mix[i].enable? 1 << i : 0;
467341f16ccSCameron Grant 	mix_setdevs(m, mask);
468341f16ccSCameron Grant 
469341f16ccSCameron Grant 	mask = 0;
470341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
471341f16ccSCameron Grant 		mask |= codec->mix[i].recidx? 1 << i : 0;
472341f16ccSCameron Grant 	mix_setrecdevs(m, mask);
473987e5972SCameron Grant 	return 0;
474987e5972SCameron Grant }
475987e5972SCameron Grant 
476987e5972SCameron Grant static int
47733dbf14aSCameron Grant ac97mix_uninit(snd_mixer *m)
47833dbf14aSCameron Grant {
47933dbf14aSCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
480341f16ccSCameron Grant 
48133dbf14aSCameron Grant 	if (codec == NULL)
48233dbf14aSCameron Grant 		return -1;
48333dbf14aSCameron Grant 	/*
48433dbf14aSCameron Grant 	if (ac97_uninitmixer(codec))
48533dbf14aSCameron Grant 		return -1;
48633dbf14aSCameron Grant 	*/
48733dbf14aSCameron Grant 	ac97_destroy(codec);
48833dbf14aSCameron Grant 	return 0;
48933dbf14aSCameron Grant }
49033dbf14aSCameron Grant 
49133dbf14aSCameron Grant static int
4929ec437a3SCameron Grant ac97mix_reinit(snd_mixer *m)
4939ec437a3SCameron Grant {
4949ec437a3SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
4959ec437a3SCameron Grant 
4969ec437a3SCameron Grant 	if (codec == NULL)
4979ec437a3SCameron Grant 		return -1;
4989ec437a3SCameron Grant 	return ac97_reinitmixer(codec);
4999ec437a3SCameron Grant }
5009ec437a3SCameron Grant 
5019ec437a3SCameron Grant static int
502987e5972SCameron Grant ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
503987e5972SCameron Grant {
504987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
505341f16ccSCameron Grant 
50639004e69SCameron Grant 	if (codec == NULL)
50739004e69SCameron Grant 		return -1;
508987e5972SCameron Grant 	return ac97_setmixer(codec, dev, left, right);
509987e5972SCameron Grant }
510987e5972SCameron Grant 
511987e5972SCameron Grant static int
512987e5972SCameron Grant ac97mix_setrecsrc(snd_mixer *m, u_int32_t src)
513987e5972SCameron Grant {
514987e5972SCameron Grant 	int i;
515987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
516341f16ccSCameron Grant 
51739004e69SCameron Grant 	if (codec == NULL)
51839004e69SCameron Grant 		return -1;
519987e5972SCameron Grant 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
52039004e69SCameron Grant 		if ((src & (1 << i)) != 0)
52139004e69SCameron Grant 			break;
522987e5972SCameron Grant 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
523987e5972SCameron Grant }
524987e5972SCameron Grant 
525987e5972SCameron Grant snd_mixer ac97_mixer = {
526987e5972SCameron Grant 	"AC97 mixer",
527987e5972SCameron Grant 	ac97mix_init,
52833dbf14aSCameron Grant 	ac97mix_uninit,
5299ec437a3SCameron Grant 	ac97mix_reinit,
530987e5972SCameron Grant 	ac97mix_set,
531987e5972SCameron Grant 	ac97mix_setrecsrc,
532987e5972SCameron Grant };
533987e5972SCameron Grant 
534