xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision 341f16cc6094a481f375fc7da0102884f73c6d5f)
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 
206987e5972SCameron Grant static int
207987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel)
208987e5972SCameron Grant {
209987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
210341f16ccSCameron Grant 
211987e5972SCameron Grant 	if (e->recidx > 0) {
212987e5972SCameron Grant 		int val = e->recidx - 1;
213987e5972SCameron Grant 		val |= val << 8;
21439004e69SCameron Grant 		wrcd(codec, AC97_REG_RECSEL, val);
215987e5972SCameron Grant 		return 0;
21639004e69SCameron Grant 	} else
21739004e69SCameron Grant 		return -1;
218987e5972SCameron Grant }
219987e5972SCameron Grant 
220987e5972SCameron Grant static int
221987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
222987e5972SCameron Grant {
223987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
224341f16ccSCameron Grant 
225341f16ccSCameron Grant 	if (e->reg && e->enable && e->bits) {
226e479a8afSCameron Grant 		int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
227987e5972SCameron Grant 
22839004e69SCameron Grant 		if (!e->stereo)
22939004e69SCameron Grant 			right = left;
230987e5972SCameron Grant 		if (e->reg > 0) {
231987e5972SCameron Grant 			left = 100 - left;
232987e5972SCameron Grant 			right = 100 - right;
233987e5972SCameron Grant 		}
234987e5972SCameron Grant 
235987e5972SCameron Grant 		max = (1 << e->bits) - 1;
236987e5972SCameron Grant 		left = (left * max) / 100;
237987e5972SCameron Grant 		right = (right * max) / 100;
238987e5972SCameron Grant 
239987e5972SCameron Grant 		val = (left << 8) | right;
240987e5972SCameron Grant 
241987e5972SCameron Grant 		left = (left * 100) / max;
242987e5972SCameron Grant 		right = (right * 100) / max;
243987e5972SCameron Grant 
244987e5972SCameron Grant 		if (e->reg > 0) {
245987e5972SCameron Grant 			left = 100 - left;
246987e5972SCameron Grant 			right = 100 - right;
247987e5972SCameron Grant 		}
248987e5972SCameron Grant 
249987e5972SCameron Grant 		if (!e->stereo) {
250987e5972SCameron Grant 			val &= max;
251987e5972SCameron Grant 			val <<= e->ofs;
252987e5972SCameron Grant 			if (e->mask) {
25339004e69SCameron Grant 				int cur = rdcd(codec, e->reg);
254987e5972SCameron Grant 				val |= cur & ~(max << e->ofs);
255987e5972SCameron Grant 			}
256987e5972SCameron Grant 		}
25739004e69SCameron Grant 		if (left == 0 && right == 0 && e->mute == 1)
25839004e69SCameron Grant 			val = AC97_MUTE;
25939004e69SCameron Grant 		wrcd(codec, reg, val);
260987e5972SCameron Grant 		return left | (right << 8);
261341f16ccSCameron Grant 	} else {
262341f16ccSCameron Grant 		/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
26339004e69SCameron Grant 		return -1;
264987e5972SCameron Grant 	}
265341f16ccSCameron Grant }
266987e5972SCameron Grant 
267987e5972SCameron Grant #if 0
268987e5972SCameron Grant static int
269987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel)
270987e5972SCameron Grant {
271987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
272987e5972SCameron Grant 	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
273987e5972SCameron Grant 		int max, val, volume;
274987e5972SCameron Grant 
275987e5972SCameron Grant 		max = (1 << e->bits) - 1;
27639004e69SCameron Grant 		val = rdcd(code, e->reg);
27739004e69SCameron Grant 		if (val == AC97_MUTE && e->mute == 1)
27839004e69SCameron Grant 			volume = 0;
279987e5972SCameron Grant 		else {
280987e5972SCameron Grant 			if (e->stereo == 0) val >>= e->ofs;
281987e5972SCameron Grant 			val &= max;
282987e5972SCameron Grant 			volume = (val * 100) / max;
283987e5972SCameron Grant 			if (e->reg > 0) volume = 100 - volume;
284987e5972SCameron Grant 		}
285987e5972SCameron Grant 		return volume;
28639004e69SCameron Grant 	} else
28739004e69SCameron Grant 		return -1;
288987e5972SCameron Grant }
289987e5972SCameron Grant #endif
290987e5972SCameron Grant 
291987e5972SCameron Grant static unsigned
29239004e69SCameron Grant ac97_initmixer(struct ac97_info *codec)
293987e5972SCameron Grant {
294341f16ccSCameron Grant 	unsigned i, j, k, old;
295987e5972SCameron Grant 	u_int32_t id;
296987e5972SCameron Grant 
29739004e69SCameron Grant 	for (i = 0; i < 32; i++)
29839004e69SCameron Grant 		codec->mix[i] = ac97mixtable_default[i];
299987e5972SCameron Grant 
30004553e63SCameron Grant 	if (codec->init) {
301cd2c103aSCameron Grant 		codec->count = codec->init(codec->devinfo);
302cd2c103aSCameron Grant 		if (codec->count == 0) {
30304553e63SCameron Grant 			device_printf(codec->dev, "ac97 codec init failed\n");
30404553e63SCameron Grant 			return ENODEV;
30504553e63SCameron Grant 		}
306cd2c103aSCameron Grant 	} else
307cd2c103aSCameron Grant 		codec->count = 1;
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 
323cd2c103aSCameron Grant 	codec->noext = 0;
324cd2c103aSCameron Grant 	codec->name = NULL;
325cd2c103aSCameron Grant 	for (i = 0; ac97codecid[i].id; i++) {
326cd2c103aSCameron Grant 		if (ac97codecid[i].id == id) {
327cd2c103aSCameron Grant 			codec->name = ac97codecid[i].name;
328cd2c103aSCameron Grant 			codec->noext = ac97codecid[i].noext;
329cd2c103aSCameron Grant 		}
330cd2c103aSCameron Grant 	}
3316b4b88f7SCameron Grant 
332cd2c103aSCameron Grant 	if (codec->noext) {
333cd2c103aSCameron Grant 		codec->extcaps = 0;
334cd2c103aSCameron Grant 		codec->extid = 0;
335cd2c103aSCameron Grant 		codec->extstat = 0;
336cd2c103aSCameron Grant 	} else {
33739004e69SCameron Grant 		i = rdcd(codec, AC97_REGEXT_ID);
33839004e69SCameron Grant 		codec->extcaps = i & 0x3fff;
33939004e69SCameron Grant 		codec->extid =  (i & 0xc000) >> 14;
34039004e69SCameron Grant 		codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
3416b4b88f7SCameron Grant 	}
342987e5972SCameron Grant 
343341f16ccSCameron Grant 	for (i = 0; i < 32; i++) {
344341f16ccSCameron Grant 		if (codec->mix[i].reg > 0) {
345341f16ccSCameron Grant 			old = rdcd(codec, codec->mix[i].reg);
346341f16ccSCameron Grant 			wrcd(codec, codec->mix[i].reg, 0x3f);
347341f16ccSCameron Grant 			j = rdcd(codec, codec->mix[i].reg);
348341f16ccSCameron Grant 			wrcd(codec, codec->mix[i].reg, old);
349341f16ccSCameron Grant 			codec->mix[i].enable = j? 1 : 0;
350341f16ccSCameron Grant 			for (k = 1; j & (1 << k); k++);
351341f16ccSCameron Grant 			codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
352341f16ccSCameron Grant 		}
353341f16ccSCameron Grant 		/* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
354341f16ccSCameron Grant 	}
355987e5972SCameron Grant 
356987e5972SCameron Grant 	if (bootverbose) {
357e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec id 0x%08x", id);
358cd2c103aSCameron Grant 		if (codec->name)
359cd2c103aSCameron Grant 			printf(" (%s)", codec->name);
36003a00905SCameron Grant 		printf("\n");
36103a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec features ");
36239004e69SCameron Grant 		for (i = j = 0; i < 10; i++)
36339004e69SCameron Grant 			if (codec->caps & (1 << i))
36439004e69SCameron Grant 				printf("%s%s", j++? ", " : "", ac97feature[i]);
36539004e69SCameron Grant 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
366987e5972SCameron Grant 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
36739004e69SCameron Grant 
36839004e69SCameron Grant 		if (codec->extcaps != 0 || codec->extid) {
36939004e69SCameron Grant 			device_printf(codec->dev, "ac97 %s codec",
37039004e69SCameron Grant 				      codec->extid? "secondary" : "primary");
37139004e69SCameron Grant 			if (codec->extcaps)
37239004e69SCameron Grant 				printf(" extended features ");
37339004e69SCameron Grant 			for (i = j = 0; i < 14; i++)
37439004e69SCameron Grant 				if (codec->extcaps & (1 << i))
37539004e69SCameron Grant 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
37639004e69SCameron Grant 			printf("\n");
37739004e69SCameron Grant 		}
378987e5972SCameron Grant 	}
379987e5972SCameron Grant 
38039004e69SCameron Grant 	if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
38103a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
382987e5972SCameron Grant 	return 0;
383987e5972SCameron Grant }
384987e5972SCameron Grant 
385987e5972SCameron Grant struct ac97_info *
38639004e69SCameron Grant ac97_create(device_t dev, void *devinfo, ac97_init *init, ac97_read *rd, ac97_write *wr)
387987e5972SCameron Grant {
388987e5972SCameron Grant 	struct ac97_info *codec;
389987e5972SCameron Grant 
390987e5972SCameron Grant 	codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT);
391987e5972SCameron Grant 	if (codec != NULL) {
39203a00905SCameron Grant 		codec->dev = dev;
39339004e69SCameron Grant 		codec->init = init;
394987e5972SCameron Grant 		codec->read = rd;
395987e5972SCameron Grant 		codec->write = wr;
396987e5972SCameron Grant 		codec->devinfo = devinfo;
397987e5972SCameron Grant 	}
398987e5972SCameron Grant 	return codec;
399987e5972SCameron Grant }
400987e5972SCameron Grant 
40133dbf14aSCameron Grant void
40233dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec)
40333dbf14aSCameron Grant {
40433dbf14aSCameron Grant 	free(codec, M_DEVBUF);
40533dbf14aSCameron Grant }
40633dbf14aSCameron Grant 
407987e5972SCameron Grant static int
408987e5972SCameron Grant ac97mix_init(snd_mixer *m)
409987e5972SCameron Grant {
410987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
411341f16ccSCameron Grant 	u_int32_t i, mask;
412341f16ccSCameron Grant 
41339004e69SCameron Grant 	if (codec == NULL)
41439004e69SCameron Grant 		return -1;
415341f16ccSCameron Grant 
416e620d959SCameron Grant 	if (ac97_initmixer(codec))
417e620d959SCameron Grant 		return -1;
418341f16ccSCameron Grant 
419341f16ccSCameron Grant 	mask = 0;
420341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
421341f16ccSCameron Grant 		mask |= codec->mix[i].enable? 1 << i : 0;
422341f16ccSCameron Grant 	mix_setdevs(m, mask);
423341f16ccSCameron Grant 
424341f16ccSCameron Grant 	mask = 0;
425341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
426341f16ccSCameron Grant 		mask |= codec->mix[i].recidx? 1 << i : 0;
427341f16ccSCameron Grant 	mix_setrecdevs(m, mask);
428987e5972SCameron Grant 	return 0;
429987e5972SCameron Grant }
430987e5972SCameron Grant 
431987e5972SCameron Grant static int
43233dbf14aSCameron Grant ac97mix_uninit(snd_mixer *m)
43333dbf14aSCameron Grant {
43433dbf14aSCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
435341f16ccSCameron Grant 
43633dbf14aSCameron Grant 	if (codec == NULL)
43733dbf14aSCameron Grant 		return -1;
43833dbf14aSCameron Grant 	/*
43933dbf14aSCameron Grant 	if (ac97_uninitmixer(codec))
44033dbf14aSCameron Grant 		return -1;
44133dbf14aSCameron Grant 	*/
44233dbf14aSCameron Grant 	ac97_destroy(codec);
44333dbf14aSCameron Grant 	return 0;
44433dbf14aSCameron Grant }
44533dbf14aSCameron Grant 
44633dbf14aSCameron Grant static int
447987e5972SCameron Grant ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
448987e5972SCameron Grant {
449987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
450341f16ccSCameron Grant 
45139004e69SCameron Grant 	if (codec == NULL)
45239004e69SCameron Grant 		return -1;
453987e5972SCameron Grant 	return ac97_setmixer(codec, dev, left, right);
454987e5972SCameron Grant }
455987e5972SCameron Grant 
456987e5972SCameron Grant static int
457987e5972SCameron Grant ac97mix_setrecsrc(snd_mixer *m, u_int32_t src)
458987e5972SCameron Grant {
459987e5972SCameron Grant 	int i;
460987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
461341f16ccSCameron Grant 
46239004e69SCameron Grant 	if (codec == NULL)
46339004e69SCameron Grant 		return -1;
464987e5972SCameron Grant 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
46539004e69SCameron Grant 		if ((src & (1 << i)) != 0)
46639004e69SCameron Grant 			break;
467987e5972SCameron Grant 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
468987e5972SCameron Grant }
469987e5972SCameron Grant 
470987e5972SCameron Grant snd_mixer ac97_mixer = {
471987e5972SCameron Grant 	"AC97 mixer",
472987e5972SCameron Grant 	ac97mix_init,
47333dbf14aSCameron Grant 	ac97mix_uninit,
474987e5972SCameron Grant 	ac97mix_set,
475987e5972SCameron Grant 	ac97mix_setrecsrc,
476987e5972SCameron Grant };
477987e5972SCameron Grant 
478