xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision 96524a52b6dea58a9f48dd99196fabf4bfd993b0)
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 {
3896524a52SOrion Hodson 	int	 reg:8;		/* register index		*/
3996524a52SOrion Hodson 				/* reg < 0 if inverted polarity	*/
4096524a52SOrion Hodson 	unsigned bits:4;	/* width of control field	*/
4196524a52SOrion Hodson 	unsigned ofs:4;		/* offset (only if stereo=0)	*/
4296524a52SOrion Hodson 	unsigned stereo:1;	/* set for stereo controls	*/
4396524a52SOrion Hodson 	unsigned mute:1;	/* bit15 is MUTE		*/
4496524a52SOrion Hodson 	unsigned recidx:4;	/* index in rec mux		*/
4596524a52SOrion Hodson 	unsigned mask:1;	/* use only masked bits		*/
4696524a52SOrion Hodson 	unsigned enable:1;	/* entry is enabled		*/
4779bb7d52SCameron Grant };
4879bb7d52SCameron Grant 
4966ef8af5SCameron Grant #define AC97_NAMELEN	16
5066ef8af5SCameron Grant struct ac97_info {
5166ef8af5SCameron Grant 	kobj_t methods;
5266ef8af5SCameron Grant 	device_t dev;
5366ef8af5SCameron Grant 	void *devinfo;
5419921b23SOrion Hodson 	u_int32_t id;
5566ef8af5SCameron Grant 	unsigned count, caps, se, extcaps, extid, extstat, noext:1;
5679bb7d52SCameron Grant 	u_int32_t flags;
5766ef8af5SCameron Grant 	struct ac97mixtable_entry mix[32];
5866ef8af5SCameron Grant 	char name[AC97_NAMELEN];
5900acb133SCameron Grant 	struct mtx *lock;
6066ef8af5SCameron Grant };
6166ef8af5SCameron Grant 
6219921b23SOrion Hodson struct ac97_vendorid {
6319921b23SOrion Hodson 	u_int32_t   id;
6419921b23SOrion Hodson 	const char *name;
6519921b23SOrion Hodson };
6619921b23SOrion Hodson 
67987e5972SCameron Grant struct ac97_codecid {
6819921b23SOrion Hodson 	u_int32_t  id;
6919921b23SOrion Hodson 	u_int8_t   stepmask;
7019921b23SOrion Hodson 	u_int8_t   noext:1;
71987e5972SCameron Grant 	char 	  *name;
72f9eb1409SOrion Hodson 	ac97_patch patch;
73987e5972SCameron Grant };
74987e5972SCameron Grant 
75987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = {
7696524a52SOrion Hodson     /*	[offset]			reg	     bits of st mu re mk en */
77341f16ccSCameron Grant 	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0, 1 },
78108082c4SOrion Hodson 	[SOUND_MIXER_MONITOR]	= { AC97_MIX_AUXOUT, 	5, 0, 1, 1, 0, 0, 0 },
79341f16ccSCameron Grant 	[SOUND_MIXER_PHONEOUT]	= { AC97_MIX_MONO, 	5, 0, 0, 1, 7, 0, 0 },
80341f16ccSCameron Grant 	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1, 0 },
81341f16ccSCameron Grant 	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1, 0 },
82341f16ccSCameron Grant 	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0, 1 },
83341f16ccSCameron Grant 	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0, 0 },
84341f16ccSCameron Grant 	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0, 1 },
85341f16ccSCameron Grant 	[SOUND_MIXER_PHONEIN]	= { AC97_MIX_PHONE, 	5, 0, 0, 1, 8, 0, 0 },
8696524a52SOrion Hodson 	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 1, 1 },
8796524a52SOrion Hodson 	/* use igain for the mic 20dB boost */
8896524a52SOrion Hodson 	[SOUND_MIXER_IGAIN] 	= { -AC97_MIX_MIC, 	1, 6, 0, 0, 0, 1, 1 },
89341f16ccSCameron Grant 	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
90341f16ccSCameron Grant 	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
91341f16ccSCameron Grant 	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
92341f16ccSCameron Grant 	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
93987e5972SCameron Grant };
94987e5972SCameron Grant 
9519921b23SOrion Hodson static const struct ac97_vendorid ac97vendorid[] = {
9619921b23SOrion Hodson 	{ 0x41445300, "Analog Devices" },
9719921b23SOrion Hodson 	{ 0x414b4d00, "Asahi Kasei" },
9819921b23SOrion Hodson 	{ 0x414c4300, "Realtek" },
9919921b23SOrion Hodson 	{ 0x414c4700, "Avance Logic" },
10019921b23SOrion Hodson 	{ 0x43525900, "Cirrus Logic" },
10119921b23SOrion Hodson 	{ 0x434d4900, "C-Media Electronics" },
10219921b23SOrion Hodson 	{ 0x43585400, "Conexant" },
10319921b23SOrion Hodson 	{ 0x45838300, "ESS Technology" },
10419921b23SOrion Hodson 	{ 0x49434500, "ICEnsemble" },
10519921b23SOrion Hodson 	{ 0x4e534300, "National Semiconductor" },
10619921b23SOrion Hodson 	{ 0x50534300, "Philips Semiconductor" },
10719921b23SOrion Hodson 	{ 0x83847600, "SigmaTel" },
10819921b23SOrion Hodson 	{ 0x53494c00, "Silicon Laboratory" },
10919921b23SOrion Hodson 	{ 0x54524100, "TriTech" },
11019921b23SOrion Hodson 	{ 0x56494100, "VIA Technologies" },
11119921b23SOrion Hodson 	{ 0x574d4c00, "Wolfson" },
11219921b23SOrion Hodson 	{ 0x594d4800, "Yamaha" },
11319921b23SOrion Hodson 	{ 0x00000000, NULL }
11419921b23SOrion Hodson };
11519921b23SOrion Hodson 
116987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = {
11719921b23SOrion Hodson 	{ 0x41445303, 0x00, 0, "AD1819",	0 },
11819921b23SOrion Hodson 	{ 0x41445340, 0x00, 0, "AD1881",	0 },
11919921b23SOrion Hodson 	{ 0x41445348, 0x00, 0, "AD1881A",	0 },
12019921b23SOrion Hodson 	{ 0x41445360, 0x00, 0, "AD1885",	0 },
12119921b23SOrion Hodson 	{ 0x41445361, 0x00, 0, "AD1886", 	ad1886_patch },
122ba548c64SOrion Hodson 	{ 0x41445362, 0x00, 0, "AD1887", 	0 },
123ba548c64SOrion Hodson 	{ 0x41445363, 0x00, 0, "AD1886A", 	0 },
124ba548c64SOrion Hodson 	{ 0x41445370, 0x00, 0, "AD1980",	0 },
125ba548c64SOrion Hodson 	{ 0x41445372, 0x00, 0, "AD1981A",	0 },
126ba548c64SOrion Hodson 	{ 0x41445374, 0x00, 0, "AD1981B",	0 },
127ba548c64SOrion Hodson 	{ 0x41445375, 0x00, 0, "AD1985",	0 },
12819921b23SOrion Hodson 	{ 0x414b4d00, 0x00, 1, "AK4540", 	0 },
12919921b23SOrion Hodson 	{ 0x414b4d01, 0x00, 1, "AK4542", 	0 },
13019921b23SOrion Hodson 	{ 0x414b4d02, 0x00, 1, "AK4543", 	0 },
13119921b23SOrion Hodson 	{ 0x414c4320, 0x0f, 0, "ALC100",	0 },
1329963235aSOrion Hodson 	{ 0x414c4730, 0x0f, 0, "ALC101",	0 },
13319921b23SOrion Hodson 	{ 0x414c4710, 0x0f, 0, "ALC200", 	0 },
13419921b23SOrion Hodson 	{ 0x414c4740, 0x0f, 0, "ALC202", 	0 },
13519921b23SOrion Hodson 	{ 0x414c4720, 0x0f, 0, "ALC650", 	0 },
13619921b23SOrion Hodson 	{ 0x43525900, 0x07, 0, "CS4297", 	0 },
13719921b23SOrion Hodson 	{ 0x43525910, 0x07, 0, "CS4297A", 	0 },
13819921b23SOrion Hodson 	{ 0x43525920, 0x07, 0, "CS4294/98",	0 },
13919921b23SOrion Hodson 	{ 0x43525930, 0x07, 0, "CS4299",	0 },
14019921b23SOrion Hodson 	{ 0x43525940, 0x07, 0, "CS4201",	0 },
141b2a0f525SOrion Hodson 	{ 0x43525958, 0x07, 0, "CS4205",	0 },
14219921b23SOrion Hodson 	{ 0x43525960, 0x07, 0, "CS4291A",	0 },
14319921b23SOrion Hodson 	{ 0x434d4961, 0x00, 0, "CMI9739",	0 },
14419921b23SOrion Hodson 	{ 0x434d4941, 0x00, 0, "CMI9738",	0 },
14519921b23SOrion Hodson 	{ 0x43585429, 0x00, 0, "CX20468",	0 },
14619921b23SOrion Hodson 	{ 0x45838308, 0x00, 0, "ES1988",	0 }, /* Formerly ES1921(?) */
14719921b23SOrion Hodson 	{ 0x49434501, 0x00, 0, "ICE1230",	0 },
14819921b23SOrion Hodson 	{ 0x49434511, 0x00, 0, "ICE1232",	0 },
14919921b23SOrion Hodson 	{ 0x49434514, 0x00, 0, "ICE1232A",	0 },
15019921b23SOrion Hodson 	{ 0x49434551, 0x00, 0, "VT1616",	0 }, /* Via badged ICE */
15119921b23SOrion Hodson 	{ 0x4e534340, 0x00, 0, "LM4540",	0 }, /* Spec blank on revid */
15219921b23SOrion Hodson 	{ 0x4e534343, 0x00, 0, "LM4543",	0 }, /* Ditto */
15319921b23SOrion Hodson 	{ 0x4e534346, 0x00, 0, "LM4546A",	0 },
15419921b23SOrion Hodson 	{ 0x4e534348, 0x00, 0, "LM4548A",	0 },
15519921b23SOrion Hodson 	{ 0x4e534331, 0x00, 0, "LM4549",	0 }, /* (?) */
15619921b23SOrion Hodson 	{ 0x4e534349, 0x00, 0, "LM4549A",	0 },
15719921b23SOrion Hodson 	{ 0x4e534350, 0x00, 0, "LM4550",	0 },
15819921b23SOrion Hodson 	{ 0x50534301, 0x00, 0, "UCB1510",	0 },
15919921b23SOrion Hodson 	{ 0x50534304, 0x00, 0, "UCB1400",	0 },
16019921b23SOrion Hodson 	{ 0x83847600, 0x00, 0, "STAC9700/83/84",	0 },
16119921b23SOrion Hodson 	{ 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
16219921b23SOrion Hodson 	{ 0x83847605, 0x00, 0, "STAC9704",		0 },
16319921b23SOrion Hodson 	{ 0x83847608, 0x00, 0, "STAC9708/11",	0 },
16419921b23SOrion Hodson 	{ 0x83847609, 0x00, 0, "STAC9721/23",	0 },
16519921b23SOrion Hodson 	{ 0x83847644, 0x00, 0, "STAC9744/45",	0 },
16619921b23SOrion Hodson 	{ 0x83847650, 0x00, 0, "STAC9750/51",	0 },
16719921b23SOrion Hodson 	{ 0x83847652, 0x00, 0, "STAC9752/53",	0 },
16819921b23SOrion Hodson 	{ 0x83847656, 0x00, 0, "STAC9756/57",	0 },
16919921b23SOrion Hodson 	{ 0x83847658, 0x00, 0, "STAC9758/59",	0 },
17019921b23SOrion Hodson 	{ 0x83847660, 0x00, 0, "STAC9760/61",	0 }, /* Extrapolated */
17119921b23SOrion Hodson 	{ 0x83847662, 0x00, 0, "STAC9762/63",	0 }, /* Extrapolated */
17219921b23SOrion Hodson 	{ 0x53494c22, 0x00, 0, "Si3036",	0 },
17319921b23SOrion Hodson 	{ 0x53494c23, 0x00, 0, "Si3038",	0 },
17419921b23SOrion Hodson 	{ 0x54524103, 0x00, 0, "TR28023",	0 }, /* Extrapolated */
17519921b23SOrion Hodson 	{ 0x54524106, 0x00, 0, "TR28026",	0 },
17619921b23SOrion Hodson 	{ 0x54524108, 0x00, 0, "TR28028",	0 },
17719921b23SOrion Hodson 	{ 0x54524123, 0x00, 0, "TR28602",	0 },
17819921b23SOrion Hodson 	{ 0x56494161, 0x00, 0, "VIA1612A",      0 },
17919921b23SOrion Hodson 	{ 0x574d4c00, 0x00, 0, "WM9701A",	0 },
18019921b23SOrion Hodson 	{ 0x574d4c03, 0x00, 0, "WM9703/4/7/8",	0 },
18119921b23SOrion Hodson 	{ 0x574d4c04, 0x00, 0, "WM9704Q",	0 },
18219921b23SOrion Hodson 	{ 0x574d4c05, 0x00, 0, "WM9705/10",	0 },
18319921b23SOrion Hodson 	{ 0x594d4800, 0x00, 0, "YMF743",	0 },
18419921b23SOrion Hodson 	{ 0x594d4802, 0x00, 0, "YMF752",	0 },
18519921b23SOrion Hodson 	{ 0x594d4803, 0x00, 0, "YMF753",	0 },
18619921b23SOrion Hodson 	{ 0, 0, 0, NULL, 0 }
187987e5972SCameron Grant };
188987e5972SCameron Grant 
189987e5972SCameron Grant static char *ac97enhancement[] = {
19004553e63SCameron Grant 	"no 3D Stereo Enhancement",
191987e5972SCameron Grant 	"Analog Devices Phat Stereo",
192987e5972SCameron Grant 	"Creative Stereo Enhancement",
193987e5972SCameron Grant 	"National Semi 3D Stereo Enhancement",
194987e5972SCameron Grant 	"Yamaha Ymersion",
195987e5972SCameron Grant 	"BBE 3D Stereo Enhancement",
196987e5972SCameron Grant 	"Crystal Semi 3D Stereo Enhancement",
197987e5972SCameron Grant 	"Qsound QXpander",
198987e5972SCameron Grant 	"Spatializer 3D Stereo Enhancement",
199987e5972SCameron Grant 	"SRS 3D Stereo Enhancement",
200987e5972SCameron Grant 	"Platform Tech 3D Stereo Enhancement",
201987e5972SCameron Grant 	"AKM 3D Audio",
202987e5972SCameron Grant 	"Aureal Stereo Enhancement",
203987e5972SCameron Grant 	"Aztech 3D Enhancement",
204987e5972SCameron Grant 	"Binaura 3D Audio Enhancement",
205987e5972SCameron Grant 	"ESS Technology Stereo Enhancement",
206987e5972SCameron Grant 	"Harman International VMAx",
207987e5972SCameron Grant 	"Nvidea 3D Stereo Enhancement",
208987e5972SCameron Grant 	"Philips Incredible Sound",
209987e5972SCameron Grant 	"Texas Instruments 3D Stereo Enhancement",
210987e5972SCameron Grant 	"VLSI Technology 3D Stereo Enhancement",
211987e5972SCameron Grant 	"TriTech 3D Stereo Enhancement",
212987e5972SCameron Grant 	"Realtek 3D Stereo Enhancement",
213987e5972SCameron Grant 	"Samsung 3D Stereo Enhancement",
214987e5972SCameron Grant 	"Wolfson Microelectronics 3D Enhancement",
215987e5972SCameron Grant 	"Delta Integration 3D Enhancement",
216987e5972SCameron Grant 	"SigmaTel 3D Enhancement",
217987e5972SCameron Grant 	"Reserved 27",
218987e5972SCameron Grant 	"Rockwell 3D Stereo Enhancement",
219987e5972SCameron Grant 	"Reserved 29",
220987e5972SCameron Grant 	"Reserved 30",
221987e5972SCameron Grant 	"Reserved 31"
222987e5972SCameron Grant };
223987e5972SCameron Grant 
224987e5972SCameron Grant static char *ac97feature[] = {
225987e5972SCameron Grant 	"mic channel",
226987e5972SCameron Grant 	"reserved",
227987e5972SCameron Grant 	"tone",
228987e5972SCameron Grant 	"simulated stereo",
229987e5972SCameron Grant 	"headphone",
230987e5972SCameron Grant 	"bass boost",
231987e5972SCameron Grant 	"18 bit DAC",
232987e5972SCameron Grant 	"20 bit DAC",
233987e5972SCameron Grant 	"18 bit ADC",
234987e5972SCameron Grant 	"20 bit ADC"
235987e5972SCameron Grant };
236987e5972SCameron Grant 
23739004e69SCameron Grant static char *ac97extfeature[] = {
23839004e69SCameron Grant 	"variable rate PCM",
23939004e69SCameron Grant 	"double rate PCM",
24039004e69SCameron Grant 	"reserved 1",
24139004e69SCameron Grant 	"variable rate mic",
24239004e69SCameron Grant 	"reserved 2",
24339004e69SCameron Grant 	"reserved 3",
24439004e69SCameron Grant 	"center DAC",
24539004e69SCameron Grant 	"surround DAC",
24639004e69SCameron Grant 	"LFE DAC",
24739004e69SCameron Grant 	"AMAP",
24839004e69SCameron Grant 	"reserved 4",
24939004e69SCameron Grant 	"reserved 5",
25039004e69SCameron Grant 	"reserved 6",
25139004e69SCameron Grant 	"reserved 7",
25239004e69SCameron Grant };
25339004e69SCameron Grant 
254f9eb1409SOrion Hodson u_int16_t
255f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg)
25639004e69SCameron Grant {
2570f55ac6cSCameron Grant 	return AC97_READ(codec->methods, codec->devinfo, reg);
25839004e69SCameron Grant }
25939004e69SCameron Grant 
260f9eb1409SOrion Hodson void
261f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
26239004e69SCameron Grant {
2630f55ac6cSCameron Grant 	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
26439004e69SCameron Grant }
26539004e69SCameron Grant 
266c6d4b83aSOrion Hodson static void
267c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec)
268c6d4b83aSOrion Hodson {
269c6d4b83aSOrion Hodson 	u_int32_t i, ps;
270f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_RESET, 0);
271c6d4b83aSOrion Hodson 	for (i = 0; i < 500; i++) {
272f9eb1409SOrion Hodson 		ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
273c6d4b83aSOrion Hodson 		if (ps == AC97_POWER_STATUS)
274c6d4b83aSOrion Hodson 			return;
275c6d4b83aSOrion Hodson 		DELAY(1000);
276c6d4b83aSOrion Hodson 	}
277a825c6e5SOrion Hodson 	device_printf(codec->dev, "AC97 reset timed out.\n");
278c6d4b83aSOrion Hodson }
279c6d4b83aSOrion Hodson 
28039004e69SCameron Grant int
28139004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate)
28239004e69SCameron Grant {
28339004e69SCameron Grant 	u_int16_t v;
28439004e69SCameron Grant 
28539004e69SCameron Grant 	switch(which) {
28639004e69SCameron Grant 	case AC97_REGEXT_FDACRATE:
28739004e69SCameron Grant 	case AC97_REGEXT_SDACRATE:
28839004e69SCameron Grant 	case AC97_REGEXT_LDACRATE:
28939004e69SCameron Grant 	case AC97_REGEXT_LADCRATE:
29039004e69SCameron Grant 	case AC97_REGEXT_MADCRATE:
29139004e69SCameron Grant 		break;
29239004e69SCameron Grant 
29339004e69SCameron Grant 	default:
29439004e69SCameron Grant 		return -1;
29539004e69SCameron Grant 	}
29639004e69SCameron Grant 
29766ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
29839004e69SCameron Grant 	if (rate != 0) {
29939004e69SCameron Grant 		v = rate;
30039004e69SCameron Grant 		if (codec->extstat & AC97_EXTCAP_DRA)
30139004e69SCameron Grant 			v >>= 1;
302f9eb1409SOrion Hodson 		ac97_wrcd(codec, which, v);
30339004e69SCameron Grant 	}
304f9eb1409SOrion Hodson 	v = ac97_rdcd(codec, which);
30539004e69SCameron Grant 	if (codec->extstat & AC97_EXTCAP_DRA)
30639004e69SCameron Grant 		v <<= 1;
30766ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
30839004e69SCameron Grant 	return v;
30939004e69SCameron Grant }
31039004e69SCameron Grant 
31139004e69SCameron Grant int
31239004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
31339004e69SCameron Grant {
31439004e69SCameron Grant 	mode &= AC97_EXTCAPS;
315647fbfebSOrion Hodson 	if ((mode & ~codec->extcaps) != 0) {
316647fbfebSOrion Hodson 		device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
317647fbfebSOrion Hodson 			      mode);
31839004e69SCameron Grant 		return -1;
319647fbfebSOrion Hodson 	}
32066ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
321f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
322f9eb1409SOrion Hodson 	codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
32366ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
32439004e69SCameron Grant 	return (mode == codec->extstat)? 0 : -1;
32539004e69SCameron Grant }
32639004e69SCameron Grant 
3279ec437a3SCameron Grant u_int16_t
3289ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec)
3299ec437a3SCameron Grant {
3309ec437a3SCameron Grant 	return codec->extstat;
3319ec437a3SCameron Grant }
3329ec437a3SCameron Grant 
3339ec437a3SCameron Grant u_int16_t
3349ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec)
3359ec437a3SCameron Grant {
3369ec437a3SCameron Grant 	return codec->extcaps;
3379ec437a3SCameron Grant }
3389ec437a3SCameron Grant 
3395d91ad67SCameron Grant u_int16_t
3405d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec)
3415d91ad67SCameron Grant {
3425d91ad67SCameron Grant 	return codec->caps;
3435d91ad67SCameron Grant }
3445d91ad67SCameron Grant 
345987e5972SCameron Grant static int
346987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel)
347987e5972SCameron Grant {
348987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
349341f16ccSCameron Grant 
350987e5972SCameron Grant 	if (e->recidx > 0) {
351987e5972SCameron Grant 		int val = e->recidx - 1;
352987e5972SCameron Grant 		val |= val << 8;
35366ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
354f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REG_RECSEL, val);
35566ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
356987e5972SCameron Grant 		return 0;
35739004e69SCameron Grant 	} else
35839004e69SCameron Grant 		return -1;
359987e5972SCameron Grant }
360987e5972SCameron Grant 
361987e5972SCameron Grant static int
362987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
363987e5972SCameron Grant {
364987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
365341f16ccSCameron Grant 
366341f16ccSCameron Grant 	if (e->reg && e->enable && e->bits) {
36796524a52SOrion Hodson 		int mask, max, val, reg;
36896524a52SOrion Hodson 
36996524a52SOrion Hodson 		reg = (e->reg >= 0) ? e->reg : -e->reg;	/* AC97 register    */
37096524a52SOrion Hodson 		max = (1 << e->bits) - 1;		/* actual range	    */
37196524a52SOrion Hodson 		mask = (max << 8) | max;		/* bits of interest */
372987e5972SCameron Grant 
37339004e69SCameron Grant 		if (!e->stereo)
37439004e69SCameron Grant 			right = left;
37596524a52SOrion Hodson 
37696524a52SOrion Hodson 		/*
37796524a52SOrion Hodson 		 * Invert the range if the polarity requires so,
37896524a52SOrion Hodson 		 * then scale to 0..max-1 to compute the value to
37996524a52SOrion Hodson 		 * write into the codec, and scale back to 0..100
38096524a52SOrion Hodson 		 * for the return value.
38196524a52SOrion Hodson 		 */
382987e5972SCameron Grant 		if (e->reg > 0) {
383987e5972SCameron Grant 			left = 100 - left;
384987e5972SCameron Grant 			right = 100 - right;
385987e5972SCameron Grant 		}
386987e5972SCameron Grant 
387987e5972SCameron Grant 		left = (left * max) / 100;
388987e5972SCameron Grant 		right = (right * max) / 100;
389987e5972SCameron Grant 
390987e5972SCameron Grant 		val = (left << 8) | right;
391987e5972SCameron Grant 
392987e5972SCameron Grant 		left = (left * 100) / max;
393987e5972SCameron Grant 		right = (right * 100) / max;
394987e5972SCameron Grant 
395987e5972SCameron Grant 		if (e->reg > 0) {
396987e5972SCameron Grant 			left = 100 - left;
397987e5972SCameron Grant 			right = 100 - right;
398987e5972SCameron Grant 		}
399987e5972SCameron Grant 
40096524a52SOrion Hodson 		/*
40196524a52SOrion Hodson 		 * For mono controls, trim val and mask, also taking
40296524a52SOrion Hodson 		 * care of e->ofs (offset of control field).
40396524a52SOrion Hodson 		 */
40496524a52SOrion Hodson 		if (e->ofs) {
405987e5972SCameron Grant 			val &= max;
406987e5972SCameron Grant 			val <<= e->ofs;
40796524a52SOrion Hodson 			mask = (max << e->ofs);
40896524a52SOrion Hodson 		}
40996524a52SOrion Hodson 
41096524a52SOrion Hodson 		/*
41196524a52SOrion Hodson 		 * If we have a mute bit, add it to the mask and
41296524a52SOrion Hodson 		 * update val and set mute if both channels require a
41396524a52SOrion Hodson 		 * zero volume.
41496524a52SOrion Hodson 		 */
41596524a52SOrion Hodson 		if (e->mute == 1) {
41696524a52SOrion Hodson 			mask |= AC97_MUTE;
41796524a52SOrion Hodson 			if (left == 0 && right == 0)
41896524a52SOrion Hodson 				val = AC97_MUTE;
41996524a52SOrion Hodson 		}
42096524a52SOrion Hodson 
42196524a52SOrion Hodson 		/*
42296524a52SOrion Hodson 		 * If the mask bit is set, do not alter the other bits.
42396524a52SOrion Hodson 		 */
42496524a52SOrion Hodson 		snd_mtxlock(codec->lock);
425987e5972SCameron Grant 		if (e->mask) {
426f9eb1409SOrion Hodson 			int cur = ac97_rdcd(codec, e->reg);
42796524a52SOrion Hodson 			val |= cur & ~(mask);
428987e5972SCameron Grant 		}
429f9eb1409SOrion Hodson 		ac97_wrcd(codec, reg, val);
43066ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
431987e5972SCameron Grant 		return left | (right << 8);
432341f16ccSCameron Grant 	} else {
433341f16ccSCameron Grant 		/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
43439004e69SCameron Grant 		return -1;
435987e5972SCameron Grant 	}
436341f16ccSCameron Grant }
437987e5972SCameron Grant 
438987e5972SCameron Grant #if 0
439987e5972SCameron Grant static int
440987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel)
441987e5972SCameron Grant {
442987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
443987e5972SCameron Grant 	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
444987e5972SCameron Grant 		int max, val, volume;
445987e5972SCameron Grant 
446987e5972SCameron Grant 		max = (1 << e->bits) - 1;
447f9eb1409SOrion Hodson 		val = ac97_rdcd(code, e->reg);
44839004e69SCameron Grant 		if (val == AC97_MUTE && e->mute == 1)
44939004e69SCameron Grant 			volume = 0;
450987e5972SCameron Grant 		else {
451987e5972SCameron Grant 			if (e->stereo == 0) val >>= e->ofs;
452987e5972SCameron Grant 			val &= max;
453987e5972SCameron Grant 			volume = (val * 100) / max;
454987e5972SCameron Grant 			if (e->reg > 0) volume = 100 - volume;
455987e5972SCameron Grant 		}
456987e5972SCameron Grant 		return volume;
45739004e69SCameron Grant 	} else
45839004e69SCameron Grant 		return -1;
459987e5972SCameron Grant }
460987e5972SCameron Grant #endif
461987e5972SCameron Grant 
462108082c4SOrion Hodson static void
463108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec)
464108082c4SOrion Hodson {
465108082c4SOrion Hodson 	/* Determine what AUXOUT really means, it can be:
466108082c4SOrion Hodson 	 *
467108082c4SOrion Hodson 	 * 1. Headphone out.
468108082c4SOrion Hodson 	 * 2. 4-Channel Out
469108082c4SOrion Hodson 	 * 3. True line level out (effectively master volume).
470108082c4SOrion Hodson 	 *
471108082c4SOrion Hodson 	 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
472108082c4SOrion Hodson 	 */
473108082c4SOrion Hodson 	if (codec->caps & AC97_CAP_HEADPHONE) {
474108082c4SOrion Hodson 		/* XXX We should probably check the AUX_OUT initial value.
475108082c4SOrion Hodson 		 * Leave AC97_MIX_AUXOUT - SOUND_MIXER_MONITOR relationship */
476108082c4SOrion Hodson 		return;
477108082c4SOrion Hodson 	} else if (codec->extcaps & AC97_EXTCAP_SDAC &&
478f9eb1409SOrion Hodson 		   ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
479108082c4SOrion Hodson 		/* 4-Channel Out, add an additional gain setting. */
480108082c4SOrion Hodson 		codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR];
481108082c4SOrion Hodson 	} else {
482108082c4SOrion Hodson 		/* Master volume is/maybe fixed in h/w, not sufficiently
483108082c4SOrion Hodson 		 * clear in spec to blat SOUND_MIXER_MASTER. */
484108082c4SOrion Hodson 		codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR];
485108082c4SOrion Hodson 	}
486108082c4SOrion Hodson 	/* Blat monitor, inappropriate label if we get here */
487108082c4SOrion Hodson 	bzero(&codec->mix[SOUND_MIXER_MONITOR],
488108082c4SOrion Hodson 	      sizeof(codec->mix[SOUND_MIXER_MONITOR]));
489108082c4SOrion Hodson }
490108082c4SOrion Hodson 
49119921b23SOrion Hodson static const char*
49219921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
49319921b23SOrion Hodson {
49419921b23SOrion Hodson 	if (cname == NULL) {
49519921b23SOrion Hodson 		sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
49619921b23SOrion Hodson 		return buf;
49719921b23SOrion Hodson 	}
49819921b23SOrion Hodson 
49919921b23SOrion Hodson 	if (vname == NULL) vname = "Unknown";
50019921b23SOrion Hodson 
50119921b23SOrion Hodson 	if (bootverbose) {
50219921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
50319921b23SOrion Hodson 	} else {
50419921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec", vname, cname);
50519921b23SOrion Hodson 	}
50619921b23SOrion Hodson 	return buf;
50719921b23SOrion Hodson }
50819921b23SOrion Hodson 
509987e5972SCameron Grant static unsigned
51039004e69SCameron Grant ac97_initmixer(struct ac97_info *codec)
511987e5972SCameron Grant {
512f9eb1409SOrion Hodson 	ac97_patch codec_patch;
51319921b23SOrion Hodson 	const char *cname, *vname;
51419921b23SOrion Hodson 	char desc[80];
51519921b23SOrion Hodson 	u_int8_t model, step;
516341f16ccSCameron Grant 	unsigned i, j, k, old;
517987e5972SCameron Grant 	u_int32_t id;
518987e5972SCameron Grant 
51966ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
5200f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
521cd2c103aSCameron Grant 	if (codec->count == 0) {
52204553e63SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
52366ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
52404553e63SCameron Grant 		return ENODEV;
52504553e63SCameron Grant 	}
5269ec437a3SCameron Grant 
527f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
528c6d4b83aSOrion Hodson 	ac97_reset(codec);
529f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
530987e5972SCameron Grant 
531f9eb1409SOrion Hodson 	i = ac97_rdcd(codec, AC97_REG_RESET);
532987e5972SCameron Grant 	codec->caps = i & 0x03ff;
533987e5972SCameron Grant 	codec->se =  (i & 0x7c00) >> 10;
534987e5972SCameron Grant 
535f9eb1409SOrion Hodson 	id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
536e620d959SCameron Grant 	if (id == 0 || id == 0xffffffff) {
537e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
53866ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
539e620d959SCameron Grant 		return ENODEV;
540e620d959SCameron Grant 	}
5416b4b88f7SCameron Grant 
54219921b23SOrion Hodson 	codec->id = id;
543cd2c103aSCameron Grant 	codec->noext = 0;
544f9eb1409SOrion Hodson 	codec_patch = NULL;
54519921b23SOrion Hodson 
54619921b23SOrion Hodson 	cname = NULL;
54719921b23SOrion Hodson 	model = step = 0;
548cd2c103aSCameron Grant 	for (i = 0; ac97codecid[i].id; i++) {
54919921b23SOrion Hodson 		u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
55019921b23SOrion Hodson 		if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
551cd2c103aSCameron Grant 			codec->noext = ac97codecid[i].noext;
552f9eb1409SOrion Hodson 			codec_patch = ac97codecid[i].patch;
55319921b23SOrion Hodson 			cname = ac97codecid[i].name;
55419921b23SOrion Hodson 			model = (id & modelmask) & 0xff;
55519921b23SOrion Hodson 			step = (id & ~modelmask) & 0xff;
55619921b23SOrion Hodson 			break;
55719921b23SOrion Hodson 		}
55819921b23SOrion Hodson 	}
55919921b23SOrion Hodson 
56019921b23SOrion Hodson 	vname = NULL;
56119921b23SOrion Hodson 	for (i = 0; ac97vendorid[i].id; i++) {
56219921b23SOrion Hodson 		if (ac97vendorid[i].id == (id & 0xffffff00)) {
56319921b23SOrion Hodson 			vname = ac97vendorid[i].name;
56419921b23SOrion Hodson 			break;
565cd2c103aSCameron Grant 		}
566cd2c103aSCameron Grant 	}
5676b4b88f7SCameron Grant 
568cd2c103aSCameron Grant 	codec->extcaps = 0;
569cd2c103aSCameron Grant 	codec->extid = 0;
570cd2c103aSCameron Grant 	codec->extstat = 0;
5716a6ee5bbSCameron Grant 	if (!codec->noext) {
572f9eb1409SOrion Hodson 		i = ac97_rdcd(codec, AC97_REGEXT_ID);
5736a6ee5bbSCameron Grant 		if (i != 0xffff) {
57439004e69SCameron Grant 			codec->extcaps = i & 0x3fff;
57539004e69SCameron Grant 			codec->extid =  (i & 0xc000) >> 14;
576f9eb1409SOrion Hodson 			codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
5776b4b88f7SCameron Grant 		}
5786a6ee5bbSCameron Grant 	}
579987e5972SCameron Grant 
580341f16ccSCameron Grant 	for (i = 0; i < 32; i++) {
581108082c4SOrion Hodson 		codec->mix[i] = ac97mixtable_default[i];
582108082c4SOrion Hodson 	}
583108082c4SOrion Hodson 	ac97_fix_auxout(codec);
584f9eb1409SOrion Hodson 	if (codec_patch)
585f9eb1409SOrion Hodson 		codec_patch(codec);
586108082c4SOrion Hodson 
587108082c4SOrion Hodson 	for (i = 0; i < 32; i++) {
58833c878f0SCameron Grant 		k = codec->noext? codec->mix[i].enable : 1;
58933c878f0SCameron Grant 		if (k && (codec->mix[i].reg > 0)) {
590f9eb1409SOrion Hodson 			old = ac97_rdcd(codec, codec->mix[i].reg);
591f9eb1409SOrion Hodson 			ac97_wrcd(codec, codec->mix[i].reg, 0x3f);
592f9eb1409SOrion Hodson 			j = ac97_rdcd(codec, codec->mix[i].reg);
593f9eb1409SOrion Hodson 			ac97_wrcd(codec, codec->mix[i].reg, old);
5946a6ee5bbSCameron Grant 			codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
595341f16ccSCameron Grant 			for (k = 1; j & (1 << k); k++);
596341f16ccSCameron Grant 			codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
597341f16ccSCameron Grant 		}
598341f16ccSCameron Grant 		/* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
599341f16ccSCameron Grant 	}
600987e5972SCameron Grant 
60119921b23SOrion Hodson 	device_printf(codec->dev, "<%s>\n",
60219921b23SOrion Hodson 		      ac97_hw_desc(codec->id, vname, cname, desc));
603a825c6e5SOrion Hodson 
604987e5972SCameron Grant 	if (bootverbose) {
60519921b23SOrion Hodson 		device_printf(codec->dev, "Codec features ");
60639004e69SCameron Grant 		for (i = j = 0; i < 10; i++)
60739004e69SCameron Grant 			if (codec->caps & (1 << i))
60839004e69SCameron Grant 				printf("%s%s", j++? ", " : "", ac97feature[i]);
60939004e69SCameron Grant 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
610987e5972SCameron Grant 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
61139004e69SCameron Grant 
61239004e69SCameron Grant 		if (codec->extcaps != 0 || codec->extid) {
61319921b23SOrion Hodson 			device_printf(codec->dev, "%s codec",
61419921b23SOrion Hodson 				      codec->extid? "Secondary" : "Primary");
61539004e69SCameron Grant 			if (codec->extcaps)
61639004e69SCameron Grant 				printf(" extended features ");
61739004e69SCameron Grant 			for (i = j = 0; i < 14; i++)
61839004e69SCameron Grant 				if (codec->extcaps & (1 << i))
61939004e69SCameron Grant 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
62039004e69SCameron Grant 			printf("\n");
62139004e69SCameron Grant 		}
622987e5972SCameron Grant 	}
623987e5972SCameron Grant 
624f9eb1409SOrion Hodson 	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
62503a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
62666ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
627987e5972SCameron Grant 	return 0;
628987e5972SCameron Grant }
629987e5972SCameron Grant 
6309ec437a3SCameron Grant static unsigned
6319ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec)
6329ec437a3SCameron Grant {
63366ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
6340f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
6359ec437a3SCameron Grant 	if (codec->count == 0) {
6369ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
63766ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
6389ec437a3SCameron Grant 		return ENODEV;
6399ec437a3SCameron Grant 	}
6409ec437a3SCameron Grant 
641f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
642c6d4b83aSOrion Hodson 	ac97_reset(codec);
643f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
6449ec437a3SCameron Grant 
6459ec437a3SCameron Grant 	if (!codec->noext) {
646f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
647f9eb1409SOrion Hodson 		if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
648b60e55dbSGuido van Rooij 		    != codec->extstat)
6499ec437a3SCameron Grant 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
650b60e55dbSGuido van Rooij 				      codec->extstat,
651f9eb1409SOrion Hodson 				      ac97_rdcd(codec, AC97_REGEXT_STAT) &
652b60e55dbSGuido van Rooij 				      AC97_EXTCAPS);
6539ec437a3SCameron Grant 	}
6549ec437a3SCameron Grant 
655f9eb1409SOrion Hodson 	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
6569ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
65766ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
6589ec437a3SCameron Grant 	return 0;
6599ec437a3SCameron Grant }
6609ec437a3SCameron Grant 
661987e5972SCameron Grant struct ac97_info *
6620f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
663987e5972SCameron Grant {
664987e5972SCameron Grant 	struct ac97_info *codec;
665987e5972SCameron Grant 
6660f55ac6cSCameron Grant 	codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
6670f55ac6cSCameron Grant 	if (codec == NULL)
6680f55ac6cSCameron Grant 		return NULL;
6690f55ac6cSCameron Grant 
67066ef8af5SCameron Grant 	snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
671489c22ebSJohn Baldwin 	codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
672a163d034SWarner Losh 	codec->methods = kobj_create(cls, M_AC97, M_WAITOK);
6730f55ac6cSCameron Grant 	if (codec->methods == NULL) {
67466ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
67566ef8af5SCameron Grant 		snd_mtxfree(codec->lock);
6760f55ac6cSCameron Grant 		free(codec, M_AC97);
6770f55ac6cSCameron Grant 		return NULL;
678987e5972SCameron Grant 	}
6790f55ac6cSCameron Grant 
6800f55ac6cSCameron Grant 	codec->dev = dev;
6810f55ac6cSCameron Grant 	codec->devinfo = devinfo;
68279bb7d52SCameron Grant 	codec->flags = 0;
683987e5972SCameron Grant 	return codec;
684987e5972SCameron Grant }
685987e5972SCameron Grant 
68633dbf14aSCameron Grant void
68733dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec)
68833dbf14aSCameron Grant {
68966ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
6900f55ac6cSCameron Grant 	if (codec->methods != NULL)
6910f55ac6cSCameron Grant 		kobj_delete(codec->methods, M_AC97);
69266ef8af5SCameron Grant 	snd_mtxfree(codec->lock);
6930f55ac6cSCameron Grant 	free(codec, M_AC97);
69433dbf14aSCameron Grant }
69533dbf14aSCameron Grant 
69679bb7d52SCameron Grant void
69779bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val)
69879bb7d52SCameron Grant {
69979bb7d52SCameron Grant 	codec->flags = val;
70079bb7d52SCameron Grant }
70179bb7d52SCameron Grant 
70279bb7d52SCameron Grant u_int32_t
70379bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec)
70479bb7d52SCameron Grant {
70579bb7d52SCameron Grant 	return codec->flags;
70679bb7d52SCameron Grant }
70779bb7d52SCameron Grant 
7080f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
7090f55ac6cSCameron Grant 
710987e5972SCameron Grant static int
71166ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m)
712987e5972SCameron Grant {
713987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
714341f16ccSCameron Grant 	u_int32_t i, mask;
715341f16ccSCameron Grant 
71639004e69SCameron Grant 	if (codec == NULL)
71739004e69SCameron Grant 		return -1;
718341f16ccSCameron Grant 
719e620d959SCameron Grant 	if (ac97_initmixer(codec))
720e620d959SCameron Grant 		return -1;
721341f16ccSCameron Grant 
722341f16ccSCameron Grant 	mask = 0;
723341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
724341f16ccSCameron Grant 		mask |= codec->mix[i].enable? 1 << i : 0;
725341f16ccSCameron Grant 	mix_setdevs(m, mask);
726341f16ccSCameron Grant 
727341f16ccSCameron Grant 	mask = 0;
728341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
729341f16ccSCameron Grant 		mask |= codec->mix[i].recidx? 1 << i : 0;
730341f16ccSCameron Grant 	mix_setrecdevs(m, mask);
731987e5972SCameron Grant 	return 0;
732987e5972SCameron Grant }
733987e5972SCameron Grant 
734987e5972SCameron Grant static int
73566ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m)
73633dbf14aSCameron Grant {
73733dbf14aSCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
738341f16ccSCameron Grant 
73933dbf14aSCameron Grant 	if (codec == NULL)
74033dbf14aSCameron Grant 		return -1;
74133dbf14aSCameron Grant 	/*
74233dbf14aSCameron Grant 	if (ac97_uninitmixer(codec))
74333dbf14aSCameron Grant 		return -1;
74433dbf14aSCameron Grant 	*/
74533dbf14aSCameron Grant 	ac97_destroy(codec);
74633dbf14aSCameron Grant 	return 0;
74733dbf14aSCameron Grant }
74833dbf14aSCameron Grant 
74933dbf14aSCameron Grant static int
75066ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m)
7519ec437a3SCameron Grant {
7529ec437a3SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
7539ec437a3SCameron Grant 
7549ec437a3SCameron Grant 	if (codec == NULL)
7559ec437a3SCameron Grant 		return -1;
7569ec437a3SCameron Grant 	return ac97_reinitmixer(codec);
7579ec437a3SCameron Grant }
7589ec437a3SCameron Grant 
7599ec437a3SCameron Grant static int
76066ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
761987e5972SCameron Grant {
762987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
763341f16ccSCameron Grant 
76439004e69SCameron Grant 	if (codec == NULL)
76539004e69SCameron Grant 		return -1;
766987e5972SCameron Grant 	return ac97_setmixer(codec, dev, left, right);
767987e5972SCameron Grant }
768987e5972SCameron Grant 
769987e5972SCameron Grant static int
77066ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
771987e5972SCameron Grant {
772987e5972SCameron Grant 	int i;
773987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
774341f16ccSCameron Grant 
77539004e69SCameron Grant 	if (codec == NULL)
77639004e69SCameron Grant 		return -1;
777987e5972SCameron Grant 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
77839004e69SCameron Grant 		if ((src & (1 << i)) != 0)
77939004e69SCameron Grant 			break;
780987e5972SCameron Grant 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
781987e5972SCameron Grant }
782987e5972SCameron Grant 
7830f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = {
7840f55ac6cSCameron Grant     	KOBJMETHOD(mixer_init,		ac97mix_init),
7850f55ac6cSCameron Grant     	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
7860f55ac6cSCameron Grant     	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
7870f55ac6cSCameron Grant     	KOBJMETHOD(mixer_set,		ac97mix_set),
7880f55ac6cSCameron Grant     	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
7890f55ac6cSCameron Grant 	{ 0, 0 }
790987e5972SCameron Grant };
7910f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer);
7920f55ac6cSCameron Grant 
7930f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
7940f55ac6cSCameron Grant 
7950f55ac6cSCameron Grant kobj_class_t
7960f55ac6cSCameron Grant ac97_getmixerclass(void)
7970f55ac6cSCameron Grant {
7980f55ac6cSCameron Grant 	return &ac97mixer_class;
7990f55ac6cSCameron Grant }
8000f55ac6cSCameron Grant 
801987e5972SCameron Grant 
802