xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision 04553e63a5a58711ea8510a970d6e754f3348e75)
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;
40987e5972SCameron Grant };
41987e5972SCameron Grant 
42987e5972SCameron Grant struct ac97_info {
4303a00905SCameron Grant 	device_t dev;
4439004e69SCameron Grant 	ac97_init *init;
45987e5972SCameron Grant 	ac97_read *read;
46987e5972SCameron Grant 	ac97_write *write;
47987e5972SCameron Grant 	void *devinfo;
48987e5972SCameron Grant 	char id[4];
49987e5972SCameron Grant 	char rev;
506b4b88f7SCameron Grant 	unsigned caps, se, extcaps, extid, extstat, noext:1;
51987e5972SCameron Grant 	struct ac97mixtable_entry mix[32];
52987e5972SCameron Grant };
53987e5972SCameron Grant 
54987e5972SCameron Grant struct ac97_codecid {
556b4b88f7SCameron Grant 	u_int32_t id, noext:1;
56987e5972SCameron Grant 	char *name;
57987e5972SCameron Grant };
58987e5972SCameron Grant 
59987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = {
60987e5972SCameron Grant 	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0 },
61987e5972SCameron Grant 	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1 },
62987e5972SCameron Grant 	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1 },
63987e5972SCameron Grant 	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0 },
64987e5972SCameron Grant 	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0 },
65987e5972SCameron Grant 	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0 },
66987e5972SCameron Grant 	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 0 },
67987e5972SCameron Grant 	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0 },
68987e5972SCameron Grant 	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0 },
69987e5972SCameron Grant 	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0 },
70987e5972SCameron Grant 	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0 }
71987e5972SCameron Grant };
72987e5972SCameron Grant 
73987e5972SCameron Grant static const unsigned ac97mixdevs =
74987e5972SCameron Grant 	SOUND_MASK_VOLUME |
75987e5972SCameron Grant 	SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE |
76987e5972SCameron Grant 	SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 |
77987e5972SCameron Grant 	SOUND_MASK_VIDEO | SOUND_MASK_RECLEV;
78987e5972SCameron Grant 
79987e5972SCameron Grant static const unsigned ac97recdevs =
80987e5972SCameron Grant 	SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC |
81987e5972SCameron Grant 	SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO;
82987e5972SCameron Grant 
83987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = {
846b4b88f7SCameron Grant 	{ 0x414b4d00, 1, "Asahi Kasei AK4540 rev 0" },
856b4b88f7SCameron Grant 	{ 0x43525900, 0, "Cirrus Logic CS4297" 	},
866b4b88f7SCameron Grant 	{ 0x83847600, 0, "SigmaTel STAC????" 	},
876b4b88f7SCameron Grant 	{ 0x83847604, 0, "SigmaTel STAC9701/3/4/5" },
886b4b88f7SCameron Grant 	{ 0x83847605, 0, "SigmaTel STAC9704" 	},
896b4b88f7SCameron Grant 	{ 0x83847608, 0, "SigmaTel STAC9708" 	},
906b4b88f7SCameron Grant 	{ 0x83847609, 0, "SigmaTel STAC9721" 	},
916b4b88f7SCameron Grant 	{ 0x414b4d01, 1, "Asahi Kasei AK4540 rev 1" },
926b4b88f7SCameron Grant 	{ 0, 	      0, NULL			}
93987e5972SCameron Grant };
94987e5972SCameron Grant 
95987e5972SCameron Grant static char *ac97enhancement[] = {
9604553e63SCameron Grant 	"no 3D Stereo Enhancement",
97987e5972SCameron Grant 	"Analog Devices Phat Stereo",
98987e5972SCameron Grant 	"Creative Stereo Enhancement",
99987e5972SCameron Grant 	"National Semi 3D Stereo Enhancement",
100987e5972SCameron Grant 	"Yamaha Ymersion",
101987e5972SCameron Grant 	"BBE 3D Stereo Enhancement",
102987e5972SCameron Grant 	"Crystal Semi 3D Stereo Enhancement",
103987e5972SCameron Grant 	"Qsound QXpander",
104987e5972SCameron Grant 	"Spatializer 3D Stereo Enhancement",
105987e5972SCameron Grant 	"SRS 3D Stereo Enhancement",
106987e5972SCameron Grant 	"Platform Tech 3D Stereo Enhancement",
107987e5972SCameron Grant 	"AKM 3D Audio",
108987e5972SCameron Grant 	"Aureal Stereo Enhancement",
109987e5972SCameron Grant 	"Aztech 3D Enhancement",
110987e5972SCameron Grant 	"Binaura 3D Audio Enhancement",
111987e5972SCameron Grant 	"ESS Technology Stereo Enhancement",
112987e5972SCameron Grant 	"Harman International VMAx",
113987e5972SCameron Grant 	"Nvidea 3D Stereo Enhancement",
114987e5972SCameron Grant 	"Philips Incredible Sound",
115987e5972SCameron Grant 	"Texas Instruments 3D Stereo Enhancement",
116987e5972SCameron Grant 	"VLSI Technology 3D Stereo Enhancement",
117987e5972SCameron Grant 	"TriTech 3D Stereo Enhancement",
118987e5972SCameron Grant 	"Realtek 3D Stereo Enhancement",
119987e5972SCameron Grant 	"Samsung 3D Stereo Enhancement",
120987e5972SCameron Grant 	"Wolfson Microelectronics 3D Enhancement",
121987e5972SCameron Grant 	"Delta Integration 3D Enhancement",
122987e5972SCameron Grant 	"SigmaTel 3D Enhancement",
123987e5972SCameron Grant 	"Reserved 27",
124987e5972SCameron Grant 	"Rockwell 3D Stereo Enhancement",
125987e5972SCameron Grant 	"Reserved 29",
126987e5972SCameron Grant 	"Reserved 30",
127987e5972SCameron Grant 	"Reserved 31"
128987e5972SCameron Grant };
129987e5972SCameron Grant 
130987e5972SCameron Grant static char *ac97feature[] = {
131987e5972SCameron Grant 	"mic channel",
132987e5972SCameron Grant 	"reserved",
133987e5972SCameron Grant 	"tone",
134987e5972SCameron Grant 	"simulated stereo",
135987e5972SCameron Grant 	"headphone",
136987e5972SCameron Grant 	"bass boost",
137987e5972SCameron Grant 	"18 bit DAC",
138987e5972SCameron Grant 	"20 bit DAC",
139987e5972SCameron Grant 	"18 bit ADC",
140987e5972SCameron Grant 	"20 bit ADC"
141987e5972SCameron Grant };
142987e5972SCameron Grant 
14339004e69SCameron Grant static char *ac97extfeature[] = {
14439004e69SCameron Grant 	"variable rate PCM",
14539004e69SCameron Grant 	"double rate PCM",
14639004e69SCameron Grant 	"reserved 1",
14739004e69SCameron Grant 	"variable rate mic",
14839004e69SCameron Grant 	"reserved 2",
14939004e69SCameron Grant 	"reserved 3",
15039004e69SCameron Grant 	"center DAC",
15139004e69SCameron Grant 	"surround DAC",
15239004e69SCameron Grant 	"LFE DAC",
15339004e69SCameron Grant 	"AMAP",
15439004e69SCameron Grant 	"reserved 4",
15539004e69SCameron Grant 	"reserved 5",
15639004e69SCameron Grant 	"reserved 6",
15739004e69SCameron Grant 	"reserved 7",
15839004e69SCameron Grant };
15939004e69SCameron Grant 
16039004e69SCameron Grant static u_int16_t
16139004e69SCameron Grant rdcd(struct ac97_info *codec, int reg)
16239004e69SCameron Grant {
16339004e69SCameron Grant 	return codec->read(codec->devinfo, reg);
16439004e69SCameron Grant }
16539004e69SCameron Grant 
16639004e69SCameron Grant static void
16739004e69SCameron Grant wrcd(struct ac97_info *codec, int reg, u_int16_t val)
16839004e69SCameron Grant {
16939004e69SCameron Grant 	codec->write(codec->devinfo, reg, val);
17039004e69SCameron Grant }
17139004e69SCameron Grant 
17239004e69SCameron Grant int
17339004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate)
17439004e69SCameron Grant {
17539004e69SCameron Grant 	u_int16_t v;
17639004e69SCameron Grant 
17739004e69SCameron Grant 	switch(which) {
17839004e69SCameron Grant 	case AC97_REGEXT_FDACRATE:
17939004e69SCameron Grant 	case AC97_REGEXT_SDACRATE:
18039004e69SCameron Grant 	case AC97_REGEXT_LDACRATE:
18139004e69SCameron Grant 	case AC97_REGEXT_LADCRATE:
18239004e69SCameron Grant 	case AC97_REGEXT_MADCRATE:
18339004e69SCameron Grant 		break;
18439004e69SCameron Grant 
18539004e69SCameron Grant 	default:
18639004e69SCameron Grant 		return -1;
18739004e69SCameron Grant 	}
18839004e69SCameron Grant 
18939004e69SCameron Grant 	if (rate != 0) {
19039004e69SCameron Grant 		v = rate;
19139004e69SCameron Grant 		if (codec->extstat & AC97_EXTCAP_DRA)
19239004e69SCameron Grant 			v >>= 1;
19339004e69SCameron Grant 		wrcd(codec, which, v);
19439004e69SCameron Grant 	}
19539004e69SCameron Grant 	v = rdcd(codec, which);
19639004e69SCameron Grant 	if (codec->extstat & AC97_EXTCAP_DRA)
19739004e69SCameron Grant 		v <<= 1;
19839004e69SCameron Grant 	return v;
19939004e69SCameron Grant }
20039004e69SCameron Grant 
20139004e69SCameron Grant int
20239004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
20339004e69SCameron Grant {
20439004e69SCameron Grant 	mode &= AC97_EXTCAPS;
20539004e69SCameron Grant 	if ((mode & ~codec->extcaps) != 0)
20639004e69SCameron Grant 		return -1;
20739004e69SCameron Grant 	wrcd(codec, AC97_REGEXT_STAT, mode);
20839004e69SCameron Grant 	codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
20939004e69SCameron Grant 	return (mode == codec->extstat)? 0 : -1;
21039004e69SCameron Grant }
21139004e69SCameron Grant 
212987e5972SCameron Grant static int
213987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel)
214987e5972SCameron Grant {
215987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
216987e5972SCameron Grant 	if (e->recidx > 0) {
217987e5972SCameron Grant 		int val = e->recidx - 1;
218987e5972SCameron Grant 		val |= val << 8;
21939004e69SCameron Grant 		wrcd(codec, AC97_REG_RECSEL, val);
220987e5972SCameron Grant 		return 0;
22139004e69SCameron Grant 	} else
22239004e69SCameron Grant 		return -1;
223987e5972SCameron Grant }
224987e5972SCameron Grant 
225987e5972SCameron Grant static int
226987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
227987e5972SCameron Grant {
228987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
229987e5972SCameron Grant 	if (e->reg != 0) {
230e479a8afSCameron Grant 		int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
231987e5972SCameron Grant 
23239004e69SCameron Grant 		if (!e->stereo)
23339004e69SCameron Grant 			right = left;
234987e5972SCameron Grant 		if (e->reg > 0) {
235987e5972SCameron Grant 			left = 100 - left;
236987e5972SCameron Grant 			right = 100 - right;
237987e5972SCameron Grant 		}
238987e5972SCameron Grant 
239987e5972SCameron Grant 		max = (1 << e->bits) - 1;
240987e5972SCameron Grant 		left = (left * max) / 100;
241987e5972SCameron Grant 		right = (right * max) / 100;
242987e5972SCameron Grant 
243987e5972SCameron Grant 		val = (left << 8) | right;
244987e5972SCameron Grant 
245987e5972SCameron Grant 		left = (left * 100) / max;
246987e5972SCameron Grant 		right = (right * 100) / max;
247987e5972SCameron Grant 
248987e5972SCameron Grant 		if (e->reg > 0) {
249987e5972SCameron Grant 			left = 100 - left;
250987e5972SCameron Grant 			right = 100 - right;
251987e5972SCameron Grant 		}
252987e5972SCameron Grant 
253987e5972SCameron Grant 		if (!e->stereo) {
254987e5972SCameron Grant 			val &= max;
255987e5972SCameron Grant 			val <<= e->ofs;
256987e5972SCameron Grant 			if (e->mask) {
25739004e69SCameron Grant 				int cur = rdcd(codec, e->reg);
258987e5972SCameron Grant 				val |= cur & ~(max << e->ofs);
259987e5972SCameron Grant 			}
260987e5972SCameron Grant 		}
26139004e69SCameron Grant 		if (left == 0 && right == 0 && e->mute == 1)
26239004e69SCameron Grant 			val = AC97_MUTE;
26339004e69SCameron Grant 		wrcd(codec, reg, val);
264987e5972SCameron Grant 		return left | (right << 8);
26539004e69SCameron Grant 	} else
26639004e69SCameron Grant 		return -1;
267987e5972SCameron Grant }
268987e5972SCameron Grant 
269987e5972SCameron Grant #if 0
270987e5972SCameron Grant static int
271987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel)
272987e5972SCameron Grant {
273987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
274987e5972SCameron Grant 	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
275987e5972SCameron Grant 		int max, val, volume;
276987e5972SCameron Grant 
277987e5972SCameron Grant 		max = (1 << e->bits) - 1;
27839004e69SCameron Grant 		val = rdcd(code, e->reg);
27939004e69SCameron Grant 		if (val == AC97_MUTE && e->mute == 1)
28039004e69SCameron Grant 			volume = 0;
281987e5972SCameron Grant 		else {
282987e5972SCameron Grant 			if (e->stereo == 0) val >>= e->ofs;
283987e5972SCameron Grant 			val &= max;
284987e5972SCameron Grant 			volume = (val * 100) / max;
285987e5972SCameron Grant 			if (e->reg > 0) volume = 100 - volume;
286987e5972SCameron Grant 		}
287987e5972SCameron Grant 		return volume;
28839004e69SCameron Grant 	} else
28939004e69SCameron Grant 		return -1;
290987e5972SCameron Grant }
291987e5972SCameron Grant #endif
292987e5972SCameron Grant 
293987e5972SCameron Grant static unsigned
29439004e69SCameron Grant ac97_initmixer(struct ac97_info *codec)
295987e5972SCameron Grant {
296987e5972SCameron Grant 	unsigned i, j;
297987e5972SCameron Grant 	u_int32_t id;
298987e5972SCameron Grant 
29939004e69SCameron Grant 	for (i = 0; i < 32; i++)
30039004e69SCameron Grant 		codec->mix[i] = ac97mixtable_default[i];
301987e5972SCameron Grant 
30204553e63SCameron Grant 	if (codec->init) {
30304553e63SCameron Grant 		if (codec->init(codec->devinfo)) {
30404553e63SCameron Grant 			device_printf(codec->dev, "ac97 codec init failed\n");
30504553e63SCameron Grant 			return ENODEV;
30604553e63SCameron Grant 		}
30704553e63SCameron Grant 	}
30839004e69SCameron Grant 	wrcd(codec, AC97_REG_POWER, 0);
30939004e69SCameron Grant 	wrcd(codec, AC97_REG_RESET, 0);
310e620d959SCameron Grant 	DELAY(100000);
311987e5972SCameron Grant 
31239004e69SCameron Grant 	i = rdcd(codec, AC97_REG_RESET);
313987e5972SCameron Grant 	codec->caps = i & 0x03ff;
314987e5972SCameron Grant 	codec->se =  (i & 0x7c00) >> 10;
315987e5972SCameron Grant 
3166b4b88f7SCameron Grant 	id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2);
3176b4b88f7SCameron Grant 	codec->rev = id & 0x000000ff;
318e620d959SCameron Grant 	if (id == 0 || id == 0xffffffff) {
319e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
320e620d959SCameron Grant 		return ENODEV;
321e620d959SCameron Grant 	}
3226b4b88f7SCameron Grant 
3236b4b88f7SCameron Grant 	for (i = 0; ac97codecid[i].id; i++)
3246b4b88f7SCameron Grant 		if (ac97codecid[i].id == id)
3256b4b88f7SCameron Grant 			codec->noext = 1;
3266b4b88f7SCameron Grant 
3276b4b88f7SCameron Grant 	if (!codec->noext) {
32839004e69SCameron Grant 		i = rdcd(codec, AC97_REGEXT_ID);
32939004e69SCameron Grant 		codec->extcaps = i & 0x3fff;
33039004e69SCameron Grant 		codec->extid =  (i & 0xc000) >> 14;
33139004e69SCameron Grant 		codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
3326b4b88f7SCameron Grant 	} else {
3336b4b88f7SCameron Grant 		codec->extcaps = 0;
3346b4b88f7SCameron Grant 		codec->extid = 0;
3356b4b88f7SCameron Grant 		codec->extstat = 0;
3366b4b88f7SCameron Grant 	}
337987e5972SCameron Grant 
33839004e69SCameron Grant 	wrcd(codec, AC97_MIX_MASTER, 0x20);
33939004e69SCameron Grant 	if ((rdcd(codec, AC97_MIX_MASTER) & 0x20) == 0x20)
340987e5972SCameron Grant 		codec->mix[SOUND_MIXER_VOLUME].bits++;
34139004e69SCameron Grant 	wrcd(codec, AC97_MIX_MASTER, 0x00);
342987e5972SCameron Grant 
343987e5972SCameron Grant 	if (bootverbose) {
344e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec id 0x%08x", id);
34539004e69SCameron Grant 		for (i = 0; ac97codecid[i].id; i++)
34639004e69SCameron Grant 			if (ac97codecid[i].id == id)
34739004e69SCameron Grant 				printf(" (%s)", ac97codecid[i].name);
34803a00905SCameron Grant 		printf("\n");
34903a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec features ");
35039004e69SCameron Grant 		for (i = j = 0; i < 10; i++)
35139004e69SCameron Grant 			if (codec->caps & (1 << i))
35239004e69SCameron Grant 				printf("%s%s", j++? ", " : "", ac97feature[i]);
35339004e69SCameron Grant 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
354987e5972SCameron Grant 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
35539004e69SCameron Grant 
35639004e69SCameron Grant 		if (codec->extcaps != 0 || codec->extid) {
35739004e69SCameron Grant 			device_printf(codec->dev, "ac97 %s codec",
35839004e69SCameron Grant 				      codec->extid? "secondary" : "primary");
35939004e69SCameron Grant 			if (codec->extcaps)
36039004e69SCameron Grant 				printf(" extended features ");
36139004e69SCameron Grant 			for (i = j = 0; i < 14; i++)
36239004e69SCameron Grant 				if (codec->extcaps & (1 << i))
36339004e69SCameron Grant 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
36439004e69SCameron Grant 			printf("\n");
36539004e69SCameron Grant 		}
366987e5972SCameron Grant 	}
367987e5972SCameron Grant 
36839004e69SCameron Grant 	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
36903a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
370987e5972SCameron Grant 	return 0;
371987e5972SCameron Grant }
372987e5972SCameron Grant 
373987e5972SCameron Grant struct ac97_info *
37439004e69SCameron Grant ac97_create(device_t dev, void *devinfo, ac97_init *init, ac97_read *rd, ac97_write *wr)
375987e5972SCameron Grant {
376987e5972SCameron Grant 	struct ac97_info *codec;
377987e5972SCameron Grant 
378987e5972SCameron Grant 	codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT);
379987e5972SCameron Grant 	if (codec != NULL) {
38003a00905SCameron Grant 		codec->dev = dev;
38139004e69SCameron Grant 		codec->init = init;
382987e5972SCameron Grant 		codec->read = rd;
383987e5972SCameron Grant 		codec->write = wr;
384987e5972SCameron Grant 		codec->devinfo = devinfo;
385987e5972SCameron Grant 	}
386987e5972SCameron Grant 	return codec;
387987e5972SCameron Grant }
388987e5972SCameron Grant 
389987e5972SCameron Grant static int
390987e5972SCameron Grant ac97mix_init(snd_mixer *m)
391987e5972SCameron Grant {
392987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
39339004e69SCameron Grant 	if (codec == NULL)
39439004e69SCameron Grant 		return -1;
395e620d959SCameron Grant 	if (ac97_initmixer(codec))
396e620d959SCameron Grant 		return -1;
397987e5972SCameron Grant 	mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0));
398987e5972SCameron Grant 	mix_setrecdevs(m, ac97recdevs);
399987e5972SCameron Grant 	return 0;
400987e5972SCameron Grant }
401987e5972SCameron Grant 
402987e5972SCameron Grant static int
403987e5972SCameron Grant ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
404987e5972SCameron Grant {
405987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
40639004e69SCameron Grant 	if (codec == NULL)
40739004e69SCameron Grant 		return -1;
408987e5972SCameron Grant 	return ac97_setmixer(codec, dev, left, right);
409987e5972SCameron Grant }
410987e5972SCameron Grant 
411987e5972SCameron Grant static int
412987e5972SCameron Grant ac97mix_setrecsrc(snd_mixer *m, u_int32_t src)
413987e5972SCameron Grant {
414987e5972SCameron Grant 	int i;
415987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
41639004e69SCameron Grant 	if (codec == NULL)
41739004e69SCameron Grant 		return -1;
418987e5972SCameron Grant 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
41939004e69SCameron Grant 		if ((src & (1 << i)) != 0)
42039004e69SCameron Grant 			break;
421987e5972SCameron Grant 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
422987e5972SCameron Grant }
423987e5972SCameron Grant 
424987e5972SCameron Grant snd_mixer ac97_mixer = {
425987e5972SCameron Grant 	"AC97 mixer",
426987e5972SCameron Grant 	ac97mix_init,
427987e5972SCameron Grant 	ac97mix_set,
428987e5972SCameron Grant 	ac97mix_setrecsrc,
429987e5972SCameron Grant };
430987e5972SCameron Grant 
431