xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision 19921b23b59edcd984704347915fac333fc5c70a)
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  */
26987e5972SCameron Grant 
27ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
28ef9308b1SCameron Grant #include <dev/sound/pcm/ac97.h>
29f9eb1409SOrion Hodson #include <dev/sound/pcm/ac97_patch.h>
30987e5972SCameron Grant 
310f55ac6cSCameron Grant #include "mixer_if.h"
32987e5972SCameron Grant 
3367b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$");
3467b1dce3SCameron Grant 
350f55ac6cSCameron Grant MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
36987e5972SCameron Grant 
3779bb7d52SCameron Grant struct ac97mixtable_entry {
3879bb7d52SCameron Grant 	int		reg:8;
3979bb7d52SCameron Grant 	unsigned	bits:4;
4079bb7d52SCameron Grant 	unsigned	ofs:4;
4179bb7d52SCameron Grant 	unsigned	stereo:1;
4279bb7d52SCameron Grant 	unsigned	mute:1;
4379bb7d52SCameron Grant 	unsigned	recidx:4;
4479bb7d52SCameron Grant 	unsigned        mask:1;
4579bb7d52SCameron Grant 	unsigned	enable:1;
4679bb7d52SCameron Grant };
4779bb7d52SCameron Grant 
4866ef8af5SCameron Grant #define AC97_NAMELEN	16
4966ef8af5SCameron Grant struct ac97_info {
5066ef8af5SCameron Grant 	kobj_t methods;
5166ef8af5SCameron Grant 	device_t dev;
5266ef8af5SCameron Grant 	void *devinfo;
5319921b23SOrion Hodson 	u_int32_t id;
5466ef8af5SCameron Grant 	unsigned count, caps, se, extcaps, extid, extstat, noext:1;
5579bb7d52SCameron Grant 	u_int32_t flags;
5666ef8af5SCameron Grant 	struct ac97mixtable_entry mix[32];
5766ef8af5SCameron Grant 	char name[AC97_NAMELEN];
5800acb133SCameron Grant 	struct mtx *lock;
5966ef8af5SCameron Grant };
6066ef8af5SCameron Grant 
6119921b23SOrion Hodson struct ac97_vendorid {
6219921b23SOrion Hodson 	u_int32_t   id;
6319921b23SOrion Hodson 	const char *name;
6419921b23SOrion Hodson };
6519921b23SOrion Hodson 
66987e5972SCameron Grant struct ac97_codecid {
6719921b23SOrion Hodson 	u_int32_t  id;
6819921b23SOrion Hodson 	u_int8_t   stepmask;
6919921b23SOrion Hodson 	u_int8_t   noext:1;
70987e5972SCameron Grant 	char 	  *name;
71f9eb1409SOrion Hodson 	ac97_patch patch;
72987e5972SCameron Grant };
73987e5972SCameron Grant 
74987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = {
75341f16ccSCameron Grant 	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0, 1 },
76108082c4SOrion Hodson 	[SOUND_MIXER_MONITOR]	= { AC97_MIX_AUXOUT, 	5, 0, 1, 1, 0, 0, 0 },
77341f16ccSCameron Grant 	[SOUND_MIXER_PHONEOUT]	= { AC97_MIX_MONO, 	5, 0, 0, 1, 7, 0, 0 },
78341f16ccSCameron Grant 	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1, 0 },
79341f16ccSCameron Grant 	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1, 0 },
80341f16ccSCameron Grant 	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0, 1 },
81341f16ccSCameron Grant 	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0, 0 },
82341f16ccSCameron Grant 	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0, 1 },
83341f16ccSCameron Grant 	[SOUND_MIXER_PHONEIN]	= { AC97_MIX_PHONE, 	5, 0, 0, 1, 8, 0, 0 },
84341f16ccSCameron Grant 	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 0, 1 },
85341f16ccSCameron Grant 	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
86341f16ccSCameron Grant 	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
87341f16ccSCameron Grant 	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
88341f16ccSCameron Grant 	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
89987e5972SCameron Grant };
90987e5972SCameron Grant 
9119921b23SOrion Hodson static const struct ac97_vendorid ac97vendorid[] = {
9219921b23SOrion Hodson 	{ 0x41445300, "Analog Devices" },
9319921b23SOrion Hodson 	{ 0x414b4d00, "Asahi Kasei" },
9419921b23SOrion Hodson 	{ 0x414c4300, "Realtek" },
9519921b23SOrion Hodson 	{ 0x414c4700, "Avance Logic" },
9619921b23SOrion Hodson 	{ 0x43525900, "Cirrus Logic" },
9719921b23SOrion Hodson 	{ 0x434d4900, "C-Media Electronics" },
9819921b23SOrion Hodson 	{ 0x43585400, "Conexant" },
9919921b23SOrion Hodson 	{ 0x45838300, "ESS Technology" },
10019921b23SOrion Hodson 	{ 0x49434500, "ICEnsemble" },
10119921b23SOrion Hodson 	{ 0x4e534300, "National Semiconductor" },
10219921b23SOrion Hodson 	{ 0x50534300, "Philips Semiconductor" },
10319921b23SOrion Hodson 	{ 0x83847600, "SigmaTel" },
10419921b23SOrion Hodson 	{ 0x53494c00, "Silicon Laboratory" },
10519921b23SOrion Hodson 	{ 0x54524100, "TriTech" },
10619921b23SOrion Hodson 	{ 0x56494100, "VIA Technologies" },
10719921b23SOrion Hodson 	{ 0x574d4c00, "Wolfson" },
10819921b23SOrion Hodson 	{ 0x594d4800, "Yamaha" },
10919921b23SOrion Hodson 	{ 0x00000000, NULL }
11019921b23SOrion Hodson };
11119921b23SOrion Hodson 
112987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = {
11319921b23SOrion Hodson 	{ 0x41445303, 0x00, 0, "AD1819",	0 },
11419921b23SOrion Hodson 	{ 0x41445340, 0x00, 0, "AD1881",	0 },
11519921b23SOrion Hodson 	{ 0x41445348, 0x00, 0, "AD1881A",	0 },
11619921b23SOrion Hodson 	{ 0x41445360, 0x00, 0, "AD1885",	0 },
11719921b23SOrion Hodson 	{ 0x41445361, 0x00, 0, "AD1886", 	ad1886_patch },
11819921b23SOrion Hodson 	{ 0x414b4d00, 0x00, 1, "AK4540", 	0 },
11919921b23SOrion Hodson 	{ 0x414b4d01, 0x00, 1, "AK4542", 	0 },
12019921b23SOrion Hodson 	{ 0x414b4d02, 0x00, 1, "AK4543", 	0 },
12119921b23SOrion Hodson 	{ 0x414c4320, 0x0f, 0, "ALC100",	0 },
12219921b23SOrion Hodson 	{ 0x414c4320, 0x0f, 0, "ALC101",	0 },
12319921b23SOrion Hodson 	{ 0x414c4710, 0x0f, 0, "ALC200", 	0 },
12419921b23SOrion Hodson 	{ 0x414c4740, 0x0f, 0, "ALC202", 	0 },
12519921b23SOrion Hodson 	{ 0x414c4720, 0x0f, 0, "ALC650", 	0 },
12619921b23SOrion Hodson 	{ 0x43525900, 0x07, 0, "CS4297", 	0 },
12719921b23SOrion Hodson 	{ 0x43525910, 0x07, 0, "CS4297A", 	0 },
12819921b23SOrion Hodson 	{ 0x43525920, 0x07, 0, "CS4294/98",	0 },
12919921b23SOrion Hodson 	{ 0x43525930, 0x07, 0, "CS4299",	0 },
13019921b23SOrion Hodson 	{ 0x43525940, 0x07, 0, "CS4201",	0 },
13119921b23SOrion Hodson 	{ 0x43525950, 0x07, 0, "CS4205",	0 },
13219921b23SOrion Hodson 	{ 0x43525960, 0x07, 0, "CS4291A",	0 },
13319921b23SOrion Hodson 	{ 0x434d4961, 0x00, 0, "CMI9739",	0 },
13419921b23SOrion Hodson 	{ 0x434d4941, 0x00, 0, "CMI9738",	0 },
13519921b23SOrion Hodson 	{ 0x43585429, 0x00, 0, "CX20468",	0 },
13619921b23SOrion Hodson 	{ 0x45838308, 0x00, 0, "ES1988",	0 }, /* Formerly ES1921(?) */
13719921b23SOrion Hodson 	{ 0x49434501, 0x00, 0, "ICE1230",	0 },
13819921b23SOrion Hodson 	{ 0x49434511, 0x00, 0, "ICE1232",	0 },
13919921b23SOrion Hodson 	{ 0x49434514, 0x00, 0, "ICE1232A",	0 },
14019921b23SOrion Hodson 	{ 0x49434551, 0x00, 0, "VT1616",	0 }, /* Via badged ICE */
14119921b23SOrion Hodson 	{ 0x4e534340, 0x00, 0, "LM4540",	0 }, /* Spec blank on revid */
14219921b23SOrion Hodson 	{ 0x4e534343, 0x00, 0, "LM4543",	0 }, /* Ditto */
14319921b23SOrion Hodson 	{ 0x4e534346, 0x00, 0, "LM4546A",	0 },
14419921b23SOrion Hodson 	{ 0x4e534348, 0x00, 0, "LM4548A",	0 },
14519921b23SOrion Hodson 	{ 0x4e534331, 0x00, 0, "LM4549",	0 }, /* (?) */
14619921b23SOrion Hodson 	{ 0x4e534349, 0x00, 0, "LM4549A",	0 },
14719921b23SOrion Hodson 	{ 0x4e534350, 0x00, 0, "LM4550",	0 },
14819921b23SOrion Hodson 	{ 0x50534301, 0x00, 0, "UCB1510",	0 },
14919921b23SOrion Hodson 	{ 0x50534304, 0x00, 0, "UCB1400",	0 },
15019921b23SOrion Hodson 	{ 0x83847600, 0x00, 0, "STAC9700/83/84",	0 },
15119921b23SOrion Hodson 	{ 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
15219921b23SOrion Hodson 	{ 0x83847605, 0x00, 0, "STAC9704",		0 },
15319921b23SOrion Hodson 	{ 0x83847608, 0x00, 0, "STAC9708/11",	0 },
15419921b23SOrion Hodson 	{ 0x83847609, 0x00, 0, "STAC9721/23",	0 },
15519921b23SOrion Hodson 	{ 0x83847644, 0x00, 0, "STAC9744/45",	0 },
15619921b23SOrion Hodson 	{ 0x83847650, 0x00, 0, "STAC9750/51",	0 },
15719921b23SOrion Hodson 	{ 0x83847652, 0x00, 0, "STAC9752/53",	0 },
15819921b23SOrion Hodson 	{ 0x83847656, 0x00, 0, "STAC9756/57",	0 },
15919921b23SOrion Hodson 	{ 0x83847658, 0x00, 0, "STAC9758/59",	0 },
16019921b23SOrion Hodson 	{ 0x83847660, 0x00, 0, "STAC9760/61",	0 }, /* Extrapolated */
16119921b23SOrion Hodson 	{ 0x83847662, 0x00, 0, "STAC9762/63",	0 }, /* Extrapolated */
16219921b23SOrion Hodson 	{ 0x53494c22, 0x00, 0, "Si3036",	0 },
16319921b23SOrion Hodson 	{ 0x53494c23, 0x00, 0, "Si3038",	0 },
16419921b23SOrion Hodson 	{ 0x54524103, 0x00, 0, "TR28023",	0 }, /* Extrapolated */
16519921b23SOrion Hodson 	{ 0x54524106, 0x00, 0, "TR28026",	0 },
16619921b23SOrion Hodson 	{ 0x54524108, 0x00, 0, "TR28028",	0 },
16719921b23SOrion Hodson 	{ 0x54524123, 0x00, 0, "TR28602",	0 },
16819921b23SOrion Hodson 	{ 0x56494161, 0x00, 0, "VIA1612A",      0 },
16919921b23SOrion Hodson 	{ 0x574d4c00, 0x00, 0, "WM9701A",	0 },
17019921b23SOrion Hodson 	{ 0x574d4c03, 0x00, 0, "WM9703/4/7/8",	0 },
17119921b23SOrion Hodson 	{ 0x574d4c04, 0x00, 0, "WM9704Q",	0 },
17219921b23SOrion Hodson 	{ 0x574d4c05, 0x00, 0, "WM9705/10",	0 },
17319921b23SOrion Hodson 	{ 0x594d4800, 0x00, 0, "YMF743",	0 },
17419921b23SOrion Hodson 	{ 0x594d4802, 0x00, 0, "YMF752",	0 },
17519921b23SOrion Hodson 	{ 0x594d4803, 0x00, 0, "YMF753",	0 },
17619921b23SOrion Hodson 	{ 0, 0, 0, NULL, 0 }
177987e5972SCameron Grant };
178987e5972SCameron Grant 
179987e5972SCameron Grant static char *ac97enhancement[] = {
18004553e63SCameron Grant 	"no 3D Stereo Enhancement",
181987e5972SCameron Grant 	"Analog Devices Phat Stereo",
182987e5972SCameron Grant 	"Creative Stereo Enhancement",
183987e5972SCameron Grant 	"National Semi 3D Stereo Enhancement",
184987e5972SCameron Grant 	"Yamaha Ymersion",
185987e5972SCameron Grant 	"BBE 3D Stereo Enhancement",
186987e5972SCameron Grant 	"Crystal Semi 3D Stereo Enhancement",
187987e5972SCameron Grant 	"Qsound QXpander",
188987e5972SCameron Grant 	"Spatializer 3D Stereo Enhancement",
189987e5972SCameron Grant 	"SRS 3D Stereo Enhancement",
190987e5972SCameron Grant 	"Platform Tech 3D Stereo Enhancement",
191987e5972SCameron Grant 	"AKM 3D Audio",
192987e5972SCameron Grant 	"Aureal Stereo Enhancement",
193987e5972SCameron Grant 	"Aztech 3D Enhancement",
194987e5972SCameron Grant 	"Binaura 3D Audio Enhancement",
195987e5972SCameron Grant 	"ESS Technology Stereo Enhancement",
196987e5972SCameron Grant 	"Harman International VMAx",
197987e5972SCameron Grant 	"Nvidea 3D Stereo Enhancement",
198987e5972SCameron Grant 	"Philips Incredible Sound",
199987e5972SCameron Grant 	"Texas Instruments 3D Stereo Enhancement",
200987e5972SCameron Grant 	"VLSI Technology 3D Stereo Enhancement",
201987e5972SCameron Grant 	"TriTech 3D Stereo Enhancement",
202987e5972SCameron Grant 	"Realtek 3D Stereo Enhancement",
203987e5972SCameron Grant 	"Samsung 3D Stereo Enhancement",
204987e5972SCameron Grant 	"Wolfson Microelectronics 3D Enhancement",
205987e5972SCameron Grant 	"Delta Integration 3D Enhancement",
206987e5972SCameron Grant 	"SigmaTel 3D Enhancement",
207987e5972SCameron Grant 	"Reserved 27",
208987e5972SCameron Grant 	"Rockwell 3D Stereo Enhancement",
209987e5972SCameron Grant 	"Reserved 29",
210987e5972SCameron Grant 	"Reserved 30",
211987e5972SCameron Grant 	"Reserved 31"
212987e5972SCameron Grant };
213987e5972SCameron Grant 
214987e5972SCameron Grant static char *ac97feature[] = {
215987e5972SCameron Grant 	"mic channel",
216987e5972SCameron Grant 	"reserved",
217987e5972SCameron Grant 	"tone",
218987e5972SCameron Grant 	"simulated stereo",
219987e5972SCameron Grant 	"headphone",
220987e5972SCameron Grant 	"bass boost",
221987e5972SCameron Grant 	"18 bit DAC",
222987e5972SCameron Grant 	"20 bit DAC",
223987e5972SCameron Grant 	"18 bit ADC",
224987e5972SCameron Grant 	"20 bit ADC"
225987e5972SCameron Grant };
226987e5972SCameron Grant 
22739004e69SCameron Grant static char *ac97extfeature[] = {
22839004e69SCameron Grant 	"variable rate PCM",
22939004e69SCameron Grant 	"double rate PCM",
23039004e69SCameron Grant 	"reserved 1",
23139004e69SCameron Grant 	"variable rate mic",
23239004e69SCameron Grant 	"reserved 2",
23339004e69SCameron Grant 	"reserved 3",
23439004e69SCameron Grant 	"center DAC",
23539004e69SCameron Grant 	"surround DAC",
23639004e69SCameron Grant 	"LFE DAC",
23739004e69SCameron Grant 	"AMAP",
23839004e69SCameron Grant 	"reserved 4",
23939004e69SCameron Grant 	"reserved 5",
24039004e69SCameron Grant 	"reserved 6",
24139004e69SCameron Grant 	"reserved 7",
24239004e69SCameron Grant };
24339004e69SCameron Grant 
244f9eb1409SOrion Hodson u_int16_t
245f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg)
24639004e69SCameron Grant {
2470f55ac6cSCameron Grant 	return AC97_READ(codec->methods, codec->devinfo, reg);
24839004e69SCameron Grant }
24939004e69SCameron Grant 
250f9eb1409SOrion Hodson void
251f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
25239004e69SCameron Grant {
2530f55ac6cSCameron Grant 	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
25439004e69SCameron Grant }
25539004e69SCameron Grant 
256c6d4b83aSOrion Hodson static void
257c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec)
258c6d4b83aSOrion Hodson {
259c6d4b83aSOrion Hodson 	u_int32_t i, ps;
260f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_RESET, 0);
261c6d4b83aSOrion Hodson 	for (i = 0; i < 500; i++) {
262f9eb1409SOrion Hodson 		ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
263c6d4b83aSOrion Hodson 		if (ps == AC97_POWER_STATUS)
264c6d4b83aSOrion Hodson 			return;
265c6d4b83aSOrion Hodson 		DELAY(1000);
266c6d4b83aSOrion Hodson 	}
267a825c6e5SOrion Hodson 	device_printf(codec->dev, "AC97 reset timed out.\n");
268c6d4b83aSOrion Hodson }
269c6d4b83aSOrion Hodson 
27039004e69SCameron Grant int
27139004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate)
27239004e69SCameron Grant {
27339004e69SCameron Grant 	u_int16_t v;
27439004e69SCameron Grant 
27539004e69SCameron Grant 	switch(which) {
27639004e69SCameron Grant 	case AC97_REGEXT_FDACRATE:
27739004e69SCameron Grant 	case AC97_REGEXT_SDACRATE:
27839004e69SCameron Grant 	case AC97_REGEXT_LDACRATE:
27939004e69SCameron Grant 	case AC97_REGEXT_LADCRATE:
28039004e69SCameron Grant 	case AC97_REGEXT_MADCRATE:
28139004e69SCameron Grant 		break;
28239004e69SCameron Grant 
28339004e69SCameron Grant 	default:
28439004e69SCameron Grant 		return -1;
28539004e69SCameron Grant 	}
28639004e69SCameron Grant 
28766ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
28839004e69SCameron Grant 	if (rate != 0) {
28939004e69SCameron Grant 		v = rate;
29039004e69SCameron Grant 		if (codec->extstat & AC97_EXTCAP_DRA)
29139004e69SCameron Grant 			v >>= 1;
292f9eb1409SOrion Hodson 		ac97_wrcd(codec, which, v);
29339004e69SCameron Grant 	}
294f9eb1409SOrion Hodson 	v = ac97_rdcd(codec, which);
29539004e69SCameron Grant 	if (codec->extstat & AC97_EXTCAP_DRA)
29639004e69SCameron Grant 		v <<= 1;
29766ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
29839004e69SCameron Grant 	return v;
29939004e69SCameron Grant }
30039004e69SCameron Grant 
30139004e69SCameron Grant int
30239004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
30339004e69SCameron Grant {
30439004e69SCameron Grant 	mode &= AC97_EXTCAPS;
305647fbfebSOrion Hodson 	if ((mode & ~codec->extcaps) != 0) {
306647fbfebSOrion Hodson 		device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
307647fbfebSOrion Hodson 			      mode);
30839004e69SCameron Grant 		return -1;
309647fbfebSOrion Hodson 	}
31066ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
311f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
312f9eb1409SOrion Hodson 	codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
31366ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
31439004e69SCameron Grant 	return (mode == codec->extstat)? 0 : -1;
31539004e69SCameron Grant }
31639004e69SCameron Grant 
3179ec437a3SCameron Grant u_int16_t
3189ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec)
3199ec437a3SCameron Grant {
3209ec437a3SCameron Grant 	return codec->extstat;
3219ec437a3SCameron Grant }
3229ec437a3SCameron Grant 
3239ec437a3SCameron Grant u_int16_t
3249ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec)
3259ec437a3SCameron Grant {
3269ec437a3SCameron Grant 	return codec->extcaps;
3279ec437a3SCameron Grant }
3289ec437a3SCameron Grant 
3295d91ad67SCameron Grant u_int16_t
3305d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec)
3315d91ad67SCameron Grant {
3325d91ad67SCameron Grant 	return codec->caps;
3335d91ad67SCameron Grant }
3345d91ad67SCameron Grant 
335987e5972SCameron Grant static int
336987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel)
337987e5972SCameron Grant {
338987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
339341f16ccSCameron Grant 
340987e5972SCameron Grant 	if (e->recidx > 0) {
341987e5972SCameron Grant 		int val = e->recidx - 1;
342987e5972SCameron Grant 		val |= val << 8;
34366ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
344f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REG_RECSEL, val);
34566ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
346987e5972SCameron Grant 		return 0;
34739004e69SCameron Grant 	} else
34839004e69SCameron Grant 		return -1;
349987e5972SCameron Grant }
350987e5972SCameron Grant 
351987e5972SCameron Grant static int
352987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
353987e5972SCameron Grant {
354987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
355341f16ccSCameron Grant 
356341f16ccSCameron Grant 	if (e->reg && e->enable && e->bits) {
357e479a8afSCameron Grant 		int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
358987e5972SCameron Grant 
35939004e69SCameron Grant 		if (!e->stereo)
36039004e69SCameron Grant 			right = left;
361987e5972SCameron Grant 		if (e->reg > 0) {
362987e5972SCameron Grant 			left = 100 - left;
363987e5972SCameron Grant 			right = 100 - right;
364987e5972SCameron Grant 		}
365987e5972SCameron Grant 
366987e5972SCameron Grant 		max = (1 << e->bits) - 1;
367987e5972SCameron Grant 		left = (left * max) / 100;
368987e5972SCameron Grant 		right = (right * max) / 100;
369987e5972SCameron Grant 
370987e5972SCameron Grant 		val = (left << 8) | right;
371987e5972SCameron Grant 
372987e5972SCameron Grant 		left = (left * 100) / max;
373987e5972SCameron Grant 		right = (right * 100) / max;
374987e5972SCameron Grant 
375987e5972SCameron Grant 		if (e->reg > 0) {
376987e5972SCameron Grant 			left = 100 - left;
377987e5972SCameron Grant 			right = 100 - right;
378987e5972SCameron Grant 		}
379987e5972SCameron Grant 
380987e5972SCameron Grant 		if (!e->stereo) {
381987e5972SCameron Grant 			val &= max;
382987e5972SCameron Grant 			val <<= e->ofs;
383987e5972SCameron Grant 			if (e->mask) {
384f9eb1409SOrion Hodson 				int cur = ac97_rdcd(codec, e->reg);
385987e5972SCameron Grant 				val |= cur & ~(max << e->ofs);
386987e5972SCameron Grant 			}
387987e5972SCameron Grant 		}
38839004e69SCameron Grant 		if (left == 0 && right == 0 && e->mute == 1)
38939004e69SCameron Grant 			val = AC97_MUTE;
39066ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
391f9eb1409SOrion Hodson 		ac97_wrcd(codec, reg, val);
39266ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
393987e5972SCameron Grant 		return left | (right << 8);
394341f16ccSCameron Grant 	} else {
395341f16ccSCameron Grant 		/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
39639004e69SCameron Grant 		return -1;
397987e5972SCameron Grant 	}
398341f16ccSCameron Grant }
399987e5972SCameron Grant 
400987e5972SCameron Grant #if 0
401987e5972SCameron Grant static int
402987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel)
403987e5972SCameron Grant {
404987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
405987e5972SCameron Grant 	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
406987e5972SCameron Grant 		int max, val, volume;
407987e5972SCameron Grant 
408987e5972SCameron Grant 		max = (1 << e->bits) - 1;
409f9eb1409SOrion Hodson 		val = ac97_rdcd(code, e->reg);
41039004e69SCameron Grant 		if (val == AC97_MUTE && e->mute == 1)
41139004e69SCameron Grant 			volume = 0;
412987e5972SCameron Grant 		else {
413987e5972SCameron Grant 			if (e->stereo == 0) val >>= e->ofs;
414987e5972SCameron Grant 			val &= max;
415987e5972SCameron Grant 			volume = (val * 100) / max;
416987e5972SCameron Grant 			if (e->reg > 0) volume = 100 - volume;
417987e5972SCameron Grant 		}
418987e5972SCameron Grant 		return volume;
41939004e69SCameron Grant 	} else
42039004e69SCameron Grant 		return -1;
421987e5972SCameron Grant }
422987e5972SCameron Grant #endif
423987e5972SCameron Grant 
424108082c4SOrion Hodson static void
425108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec)
426108082c4SOrion Hodson {
427108082c4SOrion Hodson 	/* Determine what AUXOUT really means, it can be:
428108082c4SOrion Hodson 	 *
429108082c4SOrion Hodson 	 * 1. Headphone out.
430108082c4SOrion Hodson 	 * 2. 4-Channel Out
431108082c4SOrion Hodson 	 * 3. True line level out (effectively master volume).
432108082c4SOrion Hodson 	 *
433108082c4SOrion Hodson 	 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
434108082c4SOrion Hodson 	 */
435108082c4SOrion Hodson 	if (codec->caps & AC97_CAP_HEADPHONE) {
436108082c4SOrion Hodson 		/* XXX We should probably check the AUX_OUT initial value.
437108082c4SOrion Hodson 		 * Leave AC97_MIX_AUXOUT - SOUND_MIXER_MONITOR relationship */
438108082c4SOrion Hodson 		return;
439108082c4SOrion Hodson 	} else if (codec->extcaps & AC97_EXTCAP_SDAC &&
440f9eb1409SOrion Hodson 		   ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
441108082c4SOrion Hodson 		/* 4-Channel Out, add an additional gain setting. */
442108082c4SOrion Hodson 		codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR];
443108082c4SOrion Hodson 	} else {
444108082c4SOrion Hodson 		/* Master volume is/maybe fixed in h/w, not sufficiently
445108082c4SOrion Hodson 		 * clear in spec to blat SOUND_MIXER_MASTER. */
446108082c4SOrion Hodson 		codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR];
447108082c4SOrion Hodson 	}
448108082c4SOrion Hodson 	/* Blat monitor, inappropriate label if we get here */
449108082c4SOrion Hodson 	bzero(&codec->mix[SOUND_MIXER_MONITOR],
450108082c4SOrion Hodson 	      sizeof(codec->mix[SOUND_MIXER_MONITOR]));
451108082c4SOrion Hodson }
452108082c4SOrion Hodson 
45319921b23SOrion Hodson static const char*
45419921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
45519921b23SOrion Hodson {
45619921b23SOrion Hodson 	if (cname == NULL) {
45719921b23SOrion Hodson 		sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
45819921b23SOrion Hodson 		return buf;
45919921b23SOrion Hodson 	}
46019921b23SOrion Hodson 
46119921b23SOrion Hodson 	if (vname == NULL) vname = "Unknown";
46219921b23SOrion Hodson 
46319921b23SOrion Hodson 	if (bootverbose) {
46419921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
46519921b23SOrion Hodson 	} else {
46619921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec", vname, cname);
46719921b23SOrion Hodson 	}
46819921b23SOrion Hodson 	return buf;
46919921b23SOrion Hodson }
47019921b23SOrion Hodson 
471987e5972SCameron Grant static unsigned
47239004e69SCameron Grant ac97_initmixer(struct ac97_info *codec)
473987e5972SCameron Grant {
474f9eb1409SOrion Hodson 	ac97_patch codec_patch;
47519921b23SOrion Hodson 	const char *cname, *vname;
47619921b23SOrion Hodson 	char desc[80];
47719921b23SOrion Hodson 	u_int8_t model, step;
478341f16ccSCameron Grant 	unsigned i, j, k, old;
479987e5972SCameron Grant 	u_int32_t id;
480987e5972SCameron Grant 
48166ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
4820f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
483cd2c103aSCameron Grant 	if (codec->count == 0) {
48404553e63SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
48566ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
48604553e63SCameron Grant 		return ENODEV;
48704553e63SCameron Grant 	}
4889ec437a3SCameron Grant 
489f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
490c6d4b83aSOrion Hodson 	ac97_reset(codec);
491f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
492987e5972SCameron Grant 
493f9eb1409SOrion Hodson 	i = ac97_rdcd(codec, AC97_REG_RESET);
494987e5972SCameron Grant 	codec->caps = i & 0x03ff;
495987e5972SCameron Grant 	codec->se =  (i & 0x7c00) >> 10;
496987e5972SCameron Grant 
497f9eb1409SOrion Hodson 	id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
498e620d959SCameron Grant 	if (id == 0 || id == 0xffffffff) {
499e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
50066ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
501e620d959SCameron Grant 		return ENODEV;
502e620d959SCameron Grant 	}
5036b4b88f7SCameron Grant 
50419921b23SOrion Hodson 	codec->id = id;
505cd2c103aSCameron Grant 	codec->noext = 0;
506f9eb1409SOrion Hodson 	codec_patch = NULL;
50719921b23SOrion Hodson 
50819921b23SOrion Hodson 	cname = NULL;
50919921b23SOrion Hodson 	model = step = 0;
510cd2c103aSCameron Grant 	for (i = 0; ac97codecid[i].id; i++) {
51119921b23SOrion Hodson 		u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
51219921b23SOrion Hodson 		if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
513cd2c103aSCameron Grant 			codec->noext = ac97codecid[i].noext;
514f9eb1409SOrion Hodson 			codec_patch = ac97codecid[i].patch;
51519921b23SOrion Hodson 			cname = ac97codecid[i].name;
51619921b23SOrion Hodson 			model = (id & modelmask) & 0xff;
51719921b23SOrion Hodson 			step = (id & ~modelmask) & 0xff;
51819921b23SOrion Hodson 			break;
51919921b23SOrion Hodson 		}
52019921b23SOrion Hodson 	}
52119921b23SOrion Hodson 
52219921b23SOrion Hodson 	vname = NULL;
52319921b23SOrion Hodson 	for (i = 0; ac97vendorid[i].id; i++) {
52419921b23SOrion Hodson 		if (ac97vendorid[i].id == (id & 0xffffff00)) {
52519921b23SOrion Hodson 			vname = ac97vendorid[i].name;
52619921b23SOrion Hodson 			break;
527cd2c103aSCameron Grant 		}
528cd2c103aSCameron Grant 	}
5296b4b88f7SCameron Grant 
530cd2c103aSCameron Grant 	codec->extcaps = 0;
531cd2c103aSCameron Grant 	codec->extid = 0;
532cd2c103aSCameron Grant 	codec->extstat = 0;
5336a6ee5bbSCameron Grant 	if (!codec->noext) {
534f9eb1409SOrion Hodson 		i = ac97_rdcd(codec, AC97_REGEXT_ID);
5356a6ee5bbSCameron Grant 		if (i != 0xffff) {
53639004e69SCameron Grant 			codec->extcaps = i & 0x3fff;
53739004e69SCameron Grant 			codec->extid =  (i & 0xc000) >> 14;
538f9eb1409SOrion Hodson 			codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
5396b4b88f7SCameron Grant 		}
5406a6ee5bbSCameron Grant 	}
541987e5972SCameron Grant 
542341f16ccSCameron Grant 	for (i = 0; i < 32; i++) {
543108082c4SOrion Hodson 		codec->mix[i] = ac97mixtable_default[i];
544108082c4SOrion Hodson 	}
545108082c4SOrion Hodson 	ac97_fix_auxout(codec);
546f9eb1409SOrion Hodson 	if (codec_patch)
547f9eb1409SOrion Hodson 		codec_patch(codec);
548108082c4SOrion Hodson 
549108082c4SOrion Hodson 	for (i = 0; i < 32; i++) {
55033c878f0SCameron Grant 		k = codec->noext? codec->mix[i].enable : 1;
55133c878f0SCameron Grant 		if (k && (codec->mix[i].reg > 0)) {
552f9eb1409SOrion Hodson 			old = ac97_rdcd(codec, codec->mix[i].reg);
553f9eb1409SOrion Hodson 			ac97_wrcd(codec, codec->mix[i].reg, 0x3f);
554f9eb1409SOrion Hodson 			j = ac97_rdcd(codec, codec->mix[i].reg);
555f9eb1409SOrion Hodson 			ac97_wrcd(codec, codec->mix[i].reg, old);
5566a6ee5bbSCameron Grant 			codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
557341f16ccSCameron Grant 			for (k = 1; j & (1 << k); k++);
558341f16ccSCameron Grant 			codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
559341f16ccSCameron Grant 		}
560341f16ccSCameron Grant 		/* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
561341f16ccSCameron Grant 	}
562987e5972SCameron Grant 
56319921b23SOrion Hodson 	device_printf(codec->dev, "<%s>\n",
56419921b23SOrion Hodson 		      ac97_hw_desc(codec->id, vname, cname, desc));
565a825c6e5SOrion Hodson 
566987e5972SCameron Grant 	if (bootverbose) {
56719921b23SOrion Hodson 		device_printf(codec->dev, "Codec features ");
56839004e69SCameron Grant 		for (i = j = 0; i < 10; i++)
56939004e69SCameron Grant 			if (codec->caps & (1 << i))
57039004e69SCameron Grant 				printf("%s%s", j++? ", " : "", ac97feature[i]);
57139004e69SCameron Grant 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
572987e5972SCameron Grant 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
57339004e69SCameron Grant 
57439004e69SCameron Grant 		if (codec->extcaps != 0 || codec->extid) {
57519921b23SOrion Hodson 			device_printf(codec->dev, "%s codec",
57619921b23SOrion Hodson 				      codec->extid? "Secondary" : "Primary");
57739004e69SCameron Grant 			if (codec->extcaps)
57839004e69SCameron Grant 				printf(" extended features ");
57939004e69SCameron Grant 			for (i = j = 0; i < 14; i++)
58039004e69SCameron Grant 				if (codec->extcaps & (1 << i))
58139004e69SCameron Grant 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
58239004e69SCameron Grant 			printf("\n");
58339004e69SCameron Grant 		}
584987e5972SCameron Grant 	}
585987e5972SCameron Grant 
586f9eb1409SOrion Hodson 	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
58703a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
58866ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
589987e5972SCameron Grant 	return 0;
590987e5972SCameron Grant }
591987e5972SCameron Grant 
5929ec437a3SCameron Grant static unsigned
5939ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec)
5949ec437a3SCameron Grant {
59566ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
5960f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
5979ec437a3SCameron Grant 	if (codec->count == 0) {
5989ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
59966ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
6009ec437a3SCameron Grant 		return ENODEV;
6019ec437a3SCameron Grant 	}
6029ec437a3SCameron Grant 
603f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
604c6d4b83aSOrion Hodson 	ac97_reset(codec);
605f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
6069ec437a3SCameron Grant 
6079ec437a3SCameron Grant 	if (!codec->noext) {
608f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
609f9eb1409SOrion Hodson 		if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
610b60e55dbSGuido van Rooij 		    != codec->extstat)
6119ec437a3SCameron Grant 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
612b60e55dbSGuido van Rooij 				      codec->extstat,
613f9eb1409SOrion Hodson 				      ac97_rdcd(codec, AC97_REGEXT_STAT) &
614b60e55dbSGuido van Rooij 				      AC97_EXTCAPS);
6159ec437a3SCameron Grant 	}
6169ec437a3SCameron Grant 
617f9eb1409SOrion Hodson 	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
6189ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
61966ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
6209ec437a3SCameron Grant 	return 0;
6219ec437a3SCameron Grant }
6229ec437a3SCameron Grant 
623987e5972SCameron Grant struct ac97_info *
6240f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
625987e5972SCameron Grant {
626987e5972SCameron Grant 	struct ac97_info *codec;
627987e5972SCameron Grant 
6280f55ac6cSCameron Grant 	codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
6290f55ac6cSCameron Grant 	if (codec == NULL)
6300f55ac6cSCameron Grant 		return NULL;
6310f55ac6cSCameron Grant 
63266ef8af5SCameron Grant 	snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
633489c22ebSJohn Baldwin 	codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
634a163d034SWarner Losh 	codec->methods = kobj_create(cls, M_AC97, M_WAITOK);
6350f55ac6cSCameron Grant 	if (codec->methods == NULL) {
63666ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
63766ef8af5SCameron Grant 		snd_mtxfree(codec->lock);
6380f55ac6cSCameron Grant 		free(codec, M_AC97);
6390f55ac6cSCameron Grant 		return NULL;
640987e5972SCameron Grant 	}
6410f55ac6cSCameron Grant 
6420f55ac6cSCameron Grant 	codec->dev = dev;
6430f55ac6cSCameron Grant 	codec->devinfo = devinfo;
64479bb7d52SCameron Grant 	codec->flags = 0;
645987e5972SCameron Grant 	return codec;
646987e5972SCameron Grant }
647987e5972SCameron Grant 
64833dbf14aSCameron Grant void
64933dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec)
65033dbf14aSCameron Grant {
65166ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
6520f55ac6cSCameron Grant 	if (codec->methods != NULL)
6530f55ac6cSCameron Grant 		kobj_delete(codec->methods, M_AC97);
65466ef8af5SCameron Grant 	snd_mtxfree(codec->lock);
6550f55ac6cSCameron Grant 	free(codec, M_AC97);
65633dbf14aSCameron Grant }
65733dbf14aSCameron Grant 
65879bb7d52SCameron Grant void
65979bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val)
66079bb7d52SCameron Grant {
66179bb7d52SCameron Grant 	codec->flags = val;
66279bb7d52SCameron Grant }
66379bb7d52SCameron Grant 
66479bb7d52SCameron Grant u_int32_t
66579bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec)
66679bb7d52SCameron Grant {
66779bb7d52SCameron Grant 	return codec->flags;
66879bb7d52SCameron Grant }
66979bb7d52SCameron Grant 
6700f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
6710f55ac6cSCameron Grant 
672987e5972SCameron Grant static int
67366ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m)
674987e5972SCameron Grant {
675987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
676341f16ccSCameron Grant 	u_int32_t i, mask;
677341f16ccSCameron Grant 
67839004e69SCameron Grant 	if (codec == NULL)
67939004e69SCameron Grant 		return -1;
680341f16ccSCameron Grant 
681e620d959SCameron Grant 	if (ac97_initmixer(codec))
682e620d959SCameron Grant 		return -1;
683341f16ccSCameron Grant 
684341f16ccSCameron Grant 	mask = 0;
685341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
686341f16ccSCameron Grant 		mask |= codec->mix[i].enable? 1 << i : 0;
687341f16ccSCameron Grant 	mix_setdevs(m, mask);
688341f16ccSCameron Grant 
689341f16ccSCameron Grant 	mask = 0;
690341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
691341f16ccSCameron Grant 		mask |= codec->mix[i].recidx? 1 << i : 0;
692341f16ccSCameron Grant 	mix_setrecdevs(m, mask);
693987e5972SCameron Grant 	return 0;
694987e5972SCameron Grant }
695987e5972SCameron Grant 
696987e5972SCameron Grant static int
69766ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m)
69833dbf14aSCameron Grant {
69933dbf14aSCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
700341f16ccSCameron Grant 
70133dbf14aSCameron Grant 	if (codec == NULL)
70233dbf14aSCameron Grant 		return -1;
70333dbf14aSCameron Grant 	/*
70433dbf14aSCameron Grant 	if (ac97_uninitmixer(codec))
70533dbf14aSCameron Grant 		return -1;
70633dbf14aSCameron Grant 	*/
70733dbf14aSCameron Grant 	ac97_destroy(codec);
70833dbf14aSCameron Grant 	return 0;
70933dbf14aSCameron Grant }
71033dbf14aSCameron Grant 
71133dbf14aSCameron Grant static int
71266ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m)
7139ec437a3SCameron Grant {
7149ec437a3SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
7159ec437a3SCameron Grant 
7169ec437a3SCameron Grant 	if (codec == NULL)
7179ec437a3SCameron Grant 		return -1;
7189ec437a3SCameron Grant 	return ac97_reinitmixer(codec);
7199ec437a3SCameron Grant }
7209ec437a3SCameron Grant 
7219ec437a3SCameron Grant static int
72266ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
723987e5972SCameron Grant {
724987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
725341f16ccSCameron Grant 
72639004e69SCameron Grant 	if (codec == NULL)
72739004e69SCameron Grant 		return -1;
728987e5972SCameron Grant 	return ac97_setmixer(codec, dev, left, right);
729987e5972SCameron Grant }
730987e5972SCameron Grant 
731987e5972SCameron Grant static int
73266ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
733987e5972SCameron Grant {
734987e5972SCameron Grant 	int i;
735987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
736341f16ccSCameron Grant 
73739004e69SCameron Grant 	if (codec == NULL)
73839004e69SCameron Grant 		return -1;
739987e5972SCameron Grant 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
74039004e69SCameron Grant 		if ((src & (1 << i)) != 0)
74139004e69SCameron Grant 			break;
742987e5972SCameron Grant 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
743987e5972SCameron Grant }
744987e5972SCameron Grant 
7450f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = {
7460f55ac6cSCameron Grant     	KOBJMETHOD(mixer_init,		ac97mix_init),
7470f55ac6cSCameron Grant     	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
7480f55ac6cSCameron Grant     	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
7490f55ac6cSCameron Grant     	KOBJMETHOD(mixer_set,		ac97mix_set),
7500f55ac6cSCameron Grant     	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
7510f55ac6cSCameron Grant 	{ 0, 0 }
752987e5972SCameron Grant };
7530f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer);
7540f55ac6cSCameron Grant 
7550f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
7560f55ac6cSCameron Grant 
7570f55ac6cSCameron Grant kobj_class_t
7580f55ac6cSCameron Grant ac97_getmixerclass(void)
7590f55ac6cSCameron Grant {
7600f55ac6cSCameron Grant 	return &ac97mixer_class;
7610f55ac6cSCameron Grant }
7620f55ac6cSCameron Grant 
763987e5972SCameron Grant 
764