xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision 6f0182bd0c0b742f895aa1d9748b128512013d62)
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 },
78a52604cfSOrion Hodson 	[SOUND_MIXER_OGAIN]	= { 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 },
876faa2f6dSJohn Baldwin #if 0
8896524a52SOrion Hodson 	/* use igain for the mic 20dB boost */
8996524a52SOrion Hodson 	[SOUND_MIXER_IGAIN] 	= { -AC97_MIX_MIC, 	1, 6, 0, 0, 0, 1, 1 },
906faa2f6dSJohn Baldwin #endif
91341f16ccSCameron Grant 	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
92341f16ccSCameron Grant 	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
93341f16ccSCameron Grant 	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
94341f16ccSCameron Grant 	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
95987e5972SCameron Grant };
96987e5972SCameron Grant 
9719921b23SOrion Hodson static const struct ac97_vendorid ac97vendorid[] = {
9819921b23SOrion Hodson 	{ 0x41445300, "Analog Devices" },
9919921b23SOrion Hodson 	{ 0x414b4d00, "Asahi Kasei" },
10019921b23SOrion Hodson 	{ 0x414c4300, "Realtek" },
10119921b23SOrion Hodson 	{ 0x414c4700, "Avance Logic" },
10219921b23SOrion Hodson 	{ 0x43525900, "Cirrus Logic" },
10319921b23SOrion Hodson 	{ 0x434d4900, "C-Media Electronics" },
10419921b23SOrion Hodson 	{ 0x43585400, "Conexant" },
10561a0da1dSOrion Hodson 	{ 0x454d4300, "eMicro" },
10619921b23SOrion Hodson 	{ 0x45838300, "ESS Technology" },
10719921b23SOrion Hodson 	{ 0x49434500, "ICEnsemble" },
10819921b23SOrion Hodson 	{ 0x4e534300, "National Semiconductor" },
10919921b23SOrion Hodson 	{ 0x50534300, "Philips Semiconductor" },
11019921b23SOrion Hodson 	{ 0x83847600, "SigmaTel" },
11119921b23SOrion Hodson 	{ 0x53494c00, "Silicon Laboratory" },
11219921b23SOrion Hodson 	{ 0x54524100, "TriTech" },
11319921b23SOrion Hodson 	{ 0x56494100, "VIA Technologies" },
11419921b23SOrion Hodson 	{ 0x574d4c00, "Wolfson" },
11519921b23SOrion Hodson 	{ 0x594d4800, "Yamaha" },
1166f0182bdSOrion Hodson 	{ 0x01408300, "Creative" },
11719921b23SOrion Hodson 	{ 0x00000000, NULL }
11819921b23SOrion Hodson };
11919921b23SOrion Hodson 
120987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = {
12119921b23SOrion Hodson 	{ 0x41445303, 0x00, 0, "AD1819",	0 },
12219921b23SOrion Hodson 	{ 0x41445340, 0x00, 0, "AD1881",	0 },
12319921b23SOrion Hodson 	{ 0x41445348, 0x00, 0, "AD1881A",	0 },
12419921b23SOrion Hodson 	{ 0x41445360, 0x00, 0, "AD1885",	0 },
12519921b23SOrion Hodson 	{ 0x41445361, 0x00, 0, "AD1886", 	ad1886_patch },
126ba548c64SOrion Hodson 	{ 0x41445362, 0x00, 0, "AD1887", 	0 },
127ba548c64SOrion Hodson 	{ 0x41445363, 0x00, 0, "AD1886A", 	0 },
128a52604cfSOrion Hodson 	{ 0x41445370, 0x00, 0, "AD1980",	ad198x_patch },
129ba548c64SOrion Hodson 	{ 0x41445372, 0x00, 0, "AD1981A",	0 },
130ba548c64SOrion Hodson 	{ 0x41445374, 0x00, 0, "AD1981B",	0 },
131a52604cfSOrion Hodson 	{ 0x41445375, 0x00, 0, "AD1985",	ad198x_patch },
13219921b23SOrion Hodson 	{ 0x414b4d00, 0x00, 1, "AK4540", 	0 },
13319921b23SOrion Hodson 	{ 0x414b4d01, 0x00, 1, "AK4542", 	0 },
13419921b23SOrion Hodson 	{ 0x414b4d02, 0x00, 1, "AK4543", 	0 },
13519921b23SOrion Hodson 	{ 0x414c4320, 0x0f, 0, "ALC100",	0 },
1369963235aSOrion Hodson 	{ 0x414c4730, 0x0f, 0, "ALC101",	0 },
13719921b23SOrion Hodson 	{ 0x414c4710, 0x0f, 0, "ALC200", 	0 },
13819921b23SOrion Hodson 	{ 0x414c4740, 0x0f, 0, "ALC202", 	0 },
13919921b23SOrion Hodson 	{ 0x414c4720, 0x0f, 0, "ALC650", 	0 },
14019921b23SOrion Hodson 	{ 0x43525900, 0x07, 0, "CS4297", 	0 },
14119921b23SOrion Hodson 	{ 0x43525910, 0x07, 0, "CS4297A", 	0 },
14219921b23SOrion Hodson 	{ 0x43525920, 0x07, 0, "CS4294/98",	0 },
14319921b23SOrion Hodson 	{ 0x43525930, 0x07, 0, "CS4299",	0 },
14419921b23SOrion Hodson 	{ 0x43525940, 0x07, 0, "CS4201",	0 },
145b2a0f525SOrion Hodson 	{ 0x43525958, 0x07, 0, "CS4205",	0 },
14619921b23SOrion Hodson 	{ 0x43525960, 0x07, 0, "CS4291A",	0 },
14719921b23SOrion Hodson 	{ 0x434d4961, 0x00, 0, "CMI9739",	0 },
14819921b23SOrion Hodson 	{ 0x434d4941, 0x00, 0, "CMI9738",	0 },
14919921b23SOrion Hodson 	{ 0x43585429, 0x00, 0, "CX20468",	0 },
15061a0da1dSOrion Hodson 	{ 0x454d4323, 0x00, 0, "EM28023",	0 },
15161a0da1dSOrion Hodson 	{ 0x454d4328, 0x00, 0, "EM28028",	0 },
15219921b23SOrion Hodson 	{ 0x45838308, 0x00, 0, "ES1988",	0 }, /* Formerly ES1921(?) */
15319921b23SOrion Hodson 	{ 0x49434501, 0x00, 0, "ICE1230",	0 },
15419921b23SOrion Hodson 	{ 0x49434511, 0x00, 0, "ICE1232",	0 },
15519921b23SOrion Hodson 	{ 0x49434514, 0x00, 0, "ICE1232A",	0 },
15649fd6905SOrion Hodson 	{ 0x49434551, 0x03, 0, "VT1616",	0 }, /* Via badged ICE */
15719921b23SOrion Hodson 	{ 0x4e534340, 0x00, 0, "LM4540",	0 }, /* Spec blank on revid */
15819921b23SOrion Hodson 	{ 0x4e534343, 0x00, 0, "LM4543",	0 }, /* Ditto */
15919921b23SOrion Hodson 	{ 0x4e534346, 0x00, 0, "LM4546A",	0 },
16019921b23SOrion Hodson 	{ 0x4e534348, 0x00, 0, "LM4548A",	0 },
16119921b23SOrion Hodson 	{ 0x4e534331, 0x00, 0, "LM4549",	0 }, /* (?) */
16219921b23SOrion Hodson 	{ 0x4e534349, 0x00, 0, "LM4549A",	0 },
16319921b23SOrion Hodson 	{ 0x4e534350, 0x00, 0, "LM4550",	0 },
16419921b23SOrion Hodson 	{ 0x50534301, 0x00, 0, "UCB1510",	0 },
16519921b23SOrion Hodson 	{ 0x50534304, 0x00, 0, "UCB1400",	0 },
16619921b23SOrion Hodson 	{ 0x83847600, 0x00, 0, "STAC9700/83/84",	0 },
16719921b23SOrion Hodson 	{ 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
16819921b23SOrion Hodson 	{ 0x83847605, 0x00, 0, "STAC9704",	0 },
16919921b23SOrion Hodson 	{ 0x83847608, 0x00, 0, "STAC9708/11",	0 },
17019921b23SOrion Hodson 	{ 0x83847609, 0x00, 0, "STAC9721/23",	0 },
17119921b23SOrion Hodson 	{ 0x83847644, 0x00, 0, "STAC9744/45",	0 },
17219921b23SOrion Hodson 	{ 0x83847650, 0x00, 0, "STAC9750/51",	0 },
17319921b23SOrion Hodson 	{ 0x83847652, 0x00, 0, "STAC9752/53",	0 },
17419921b23SOrion Hodson 	{ 0x83847656, 0x00, 0, "STAC9756/57",	0 },
17519921b23SOrion Hodson 	{ 0x83847658, 0x00, 0, "STAC9758/59",	0 },
17619921b23SOrion Hodson 	{ 0x83847660, 0x00, 0, "STAC9760/61",	0 }, /* Extrapolated */
17719921b23SOrion Hodson 	{ 0x83847662, 0x00, 0, "STAC9762/63",	0 }, /* Extrapolated */
17819921b23SOrion Hodson 	{ 0x53494c22, 0x00, 0, "Si3036",	0 },
17919921b23SOrion Hodson 	{ 0x53494c23, 0x00, 0, "Si3038",	0 },
18019921b23SOrion Hodson 	{ 0x54524103, 0x00, 0, "TR28023",	0 }, /* Extrapolated */
18119921b23SOrion Hodson 	{ 0x54524106, 0x00, 0, "TR28026",	0 },
18219921b23SOrion Hodson 	{ 0x54524108, 0x00, 0, "TR28028",	0 },
18319921b23SOrion Hodson 	{ 0x54524123, 0x00, 0, "TR28602",	0 },
18419921b23SOrion Hodson 	{ 0x56494161, 0x00, 0, "VIA1612A",      0 },
18519921b23SOrion Hodson 	{ 0x574d4c00, 0x00, 0, "WM9701A",	0 },
18619921b23SOrion Hodson 	{ 0x574d4c03, 0x00, 0, "WM9703/4/7/8",	0 },
18719921b23SOrion Hodson 	{ 0x574d4c04, 0x00, 0, "WM9704Q",	0 },
18819921b23SOrion Hodson 	{ 0x574d4c05, 0x00, 0, "WM9705/10",	0 },
18919921b23SOrion Hodson 	{ 0x594d4800, 0x00, 0, "YMF743",	0 },
19019921b23SOrion Hodson 	{ 0x594d4802, 0x00, 0, "YMF752",	0 },
19119921b23SOrion Hodson 	{ 0x594d4803, 0x00, 0, "YMF753",	0 },
1926f0182bdSOrion Hodson 	{ 0x01408384, 0x00, 0, "EV1938",	0 },
19319921b23SOrion Hodson 	{ 0, 0, 0, NULL, 0 }
194987e5972SCameron Grant };
195987e5972SCameron Grant 
196987e5972SCameron Grant static char *ac97enhancement[] = {
19704553e63SCameron Grant 	"no 3D Stereo Enhancement",
198987e5972SCameron Grant 	"Analog Devices Phat Stereo",
199987e5972SCameron Grant 	"Creative Stereo Enhancement",
200987e5972SCameron Grant 	"National Semi 3D Stereo Enhancement",
201987e5972SCameron Grant 	"Yamaha Ymersion",
202987e5972SCameron Grant 	"BBE 3D Stereo Enhancement",
203987e5972SCameron Grant 	"Crystal Semi 3D Stereo Enhancement",
204987e5972SCameron Grant 	"Qsound QXpander",
205987e5972SCameron Grant 	"Spatializer 3D Stereo Enhancement",
206987e5972SCameron Grant 	"SRS 3D Stereo Enhancement",
207987e5972SCameron Grant 	"Platform Tech 3D Stereo Enhancement",
208987e5972SCameron Grant 	"AKM 3D Audio",
209987e5972SCameron Grant 	"Aureal Stereo Enhancement",
210987e5972SCameron Grant 	"Aztech 3D Enhancement",
211987e5972SCameron Grant 	"Binaura 3D Audio Enhancement",
212987e5972SCameron Grant 	"ESS Technology Stereo Enhancement",
213987e5972SCameron Grant 	"Harman International VMAx",
214987e5972SCameron Grant 	"Nvidea 3D Stereo Enhancement",
215987e5972SCameron Grant 	"Philips Incredible Sound",
216987e5972SCameron Grant 	"Texas Instruments 3D Stereo Enhancement",
217987e5972SCameron Grant 	"VLSI Technology 3D Stereo Enhancement",
218987e5972SCameron Grant 	"TriTech 3D Stereo Enhancement",
219987e5972SCameron Grant 	"Realtek 3D Stereo Enhancement",
220987e5972SCameron Grant 	"Samsung 3D Stereo Enhancement",
221987e5972SCameron Grant 	"Wolfson Microelectronics 3D Enhancement",
222987e5972SCameron Grant 	"Delta Integration 3D Enhancement",
223987e5972SCameron Grant 	"SigmaTel 3D Enhancement",
224987e5972SCameron Grant 	"Reserved 27",
225987e5972SCameron Grant 	"Rockwell 3D Stereo Enhancement",
226987e5972SCameron Grant 	"Reserved 29",
227987e5972SCameron Grant 	"Reserved 30",
228987e5972SCameron Grant 	"Reserved 31"
229987e5972SCameron Grant };
230987e5972SCameron Grant 
231987e5972SCameron Grant static char *ac97feature[] = {
232987e5972SCameron Grant 	"mic channel",
233987e5972SCameron Grant 	"reserved",
234987e5972SCameron Grant 	"tone",
235987e5972SCameron Grant 	"simulated stereo",
236987e5972SCameron Grant 	"headphone",
237987e5972SCameron Grant 	"bass boost",
238987e5972SCameron Grant 	"18 bit DAC",
239987e5972SCameron Grant 	"20 bit DAC",
240987e5972SCameron Grant 	"18 bit ADC",
241987e5972SCameron Grant 	"20 bit ADC"
242987e5972SCameron Grant };
243987e5972SCameron Grant 
24439004e69SCameron Grant static char *ac97extfeature[] = {
24539004e69SCameron Grant 	"variable rate PCM",
24639004e69SCameron Grant 	"double rate PCM",
24739004e69SCameron Grant 	"reserved 1",
24839004e69SCameron Grant 	"variable rate mic",
24939004e69SCameron Grant 	"reserved 2",
25039004e69SCameron Grant 	"reserved 3",
25139004e69SCameron Grant 	"center DAC",
25239004e69SCameron Grant 	"surround DAC",
25339004e69SCameron Grant 	"LFE DAC",
25439004e69SCameron Grant 	"AMAP",
25539004e69SCameron Grant 	"reserved 4",
25639004e69SCameron Grant 	"reserved 5",
25739004e69SCameron Grant 	"reserved 6",
25839004e69SCameron Grant 	"reserved 7",
25939004e69SCameron Grant };
26039004e69SCameron Grant 
261f9eb1409SOrion Hodson u_int16_t
262f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg)
26339004e69SCameron Grant {
2640f55ac6cSCameron Grant 	return AC97_READ(codec->methods, codec->devinfo, reg);
26539004e69SCameron Grant }
26639004e69SCameron Grant 
267f9eb1409SOrion Hodson void
268f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
26939004e69SCameron Grant {
2700f55ac6cSCameron Grant 	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
27139004e69SCameron Grant }
27239004e69SCameron Grant 
273c6d4b83aSOrion Hodson static void
274c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec)
275c6d4b83aSOrion Hodson {
276c6d4b83aSOrion Hodson 	u_int32_t i, ps;
277f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_RESET, 0);
278c6d4b83aSOrion Hodson 	for (i = 0; i < 500; i++) {
279f9eb1409SOrion Hodson 		ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
280c6d4b83aSOrion Hodson 		if (ps == AC97_POWER_STATUS)
281c6d4b83aSOrion Hodson 			return;
282c6d4b83aSOrion Hodson 		DELAY(1000);
283c6d4b83aSOrion Hodson 	}
284a825c6e5SOrion Hodson 	device_printf(codec->dev, "AC97 reset timed out.\n");
285c6d4b83aSOrion Hodson }
286c6d4b83aSOrion Hodson 
28739004e69SCameron Grant int
28839004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate)
28939004e69SCameron Grant {
29039004e69SCameron Grant 	u_int16_t v;
29139004e69SCameron Grant 
29239004e69SCameron Grant 	switch(which) {
29339004e69SCameron Grant 	case AC97_REGEXT_FDACRATE:
29439004e69SCameron Grant 	case AC97_REGEXT_SDACRATE:
29539004e69SCameron Grant 	case AC97_REGEXT_LDACRATE:
29639004e69SCameron Grant 	case AC97_REGEXT_LADCRATE:
29739004e69SCameron Grant 	case AC97_REGEXT_MADCRATE:
29839004e69SCameron Grant 		break;
29939004e69SCameron Grant 
30039004e69SCameron Grant 	default:
30139004e69SCameron Grant 		return -1;
30239004e69SCameron Grant 	}
30339004e69SCameron Grant 
30466ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
30539004e69SCameron Grant 	if (rate != 0) {
30639004e69SCameron Grant 		v = rate;
30739004e69SCameron Grant 		if (codec->extstat & AC97_EXTCAP_DRA)
30839004e69SCameron Grant 			v >>= 1;
309f9eb1409SOrion Hodson 		ac97_wrcd(codec, which, v);
31039004e69SCameron Grant 	}
311f9eb1409SOrion Hodson 	v = ac97_rdcd(codec, which);
31239004e69SCameron Grant 	if (codec->extstat & AC97_EXTCAP_DRA)
31339004e69SCameron Grant 		v <<= 1;
31466ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
31539004e69SCameron Grant 	return v;
31639004e69SCameron Grant }
31739004e69SCameron Grant 
31839004e69SCameron Grant int
31939004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
32039004e69SCameron Grant {
32139004e69SCameron Grant 	mode &= AC97_EXTCAPS;
322647fbfebSOrion Hodson 	if ((mode & ~codec->extcaps) != 0) {
323647fbfebSOrion Hodson 		device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
324647fbfebSOrion Hodson 			      mode);
32539004e69SCameron Grant 		return -1;
326647fbfebSOrion Hodson 	}
32766ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
328f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
329f9eb1409SOrion Hodson 	codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
33066ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
33139004e69SCameron Grant 	return (mode == codec->extstat)? 0 : -1;
33239004e69SCameron Grant }
33339004e69SCameron Grant 
3349ec437a3SCameron Grant u_int16_t
3359ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec)
3369ec437a3SCameron Grant {
3379ec437a3SCameron Grant 	return codec->extstat;
3389ec437a3SCameron Grant }
3399ec437a3SCameron Grant 
3409ec437a3SCameron Grant u_int16_t
3419ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec)
3429ec437a3SCameron Grant {
3439ec437a3SCameron Grant 	return codec->extcaps;
3449ec437a3SCameron Grant }
3459ec437a3SCameron Grant 
3465d91ad67SCameron Grant u_int16_t
3475d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec)
3485d91ad67SCameron Grant {
3495d91ad67SCameron Grant 	return codec->caps;
3505d91ad67SCameron Grant }
3515d91ad67SCameron Grant 
352987e5972SCameron Grant static int
353987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel)
354987e5972SCameron Grant {
355987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
356341f16ccSCameron Grant 
357987e5972SCameron Grant 	if (e->recidx > 0) {
358987e5972SCameron Grant 		int val = e->recidx - 1;
359987e5972SCameron Grant 		val |= val << 8;
36066ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
361f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REG_RECSEL, val);
36266ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
363987e5972SCameron Grant 		return 0;
36439004e69SCameron Grant 	} else
36539004e69SCameron Grant 		return -1;
366987e5972SCameron Grant }
367987e5972SCameron Grant 
368987e5972SCameron Grant static int
369987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
370987e5972SCameron Grant {
371987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
372341f16ccSCameron Grant 
373341f16ccSCameron Grant 	if (e->reg && e->enable && e->bits) {
37496524a52SOrion Hodson 		int mask, max, val, reg;
37596524a52SOrion Hodson 
37696524a52SOrion Hodson 		reg = (e->reg >= 0) ? e->reg : -e->reg;	/* AC97 register    */
37796524a52SOrion Hodson 		max = (1 << e->bits) - 1;		/* actual range	    */
37896524a52SOrion Hodson 		mask = (max << 8) | max;		/* bits of interest */
379987e5972SCameron Grant 
38039004e69SCameron Grant 		if (!e->stereo)
38139004e69SCameron Grant 			right = left;
38296524a52SOrion Hodson 
38396524a52SOrion Hodson 		/*
38496524a52SOrion Hodson 		 * Invert the range if the polarity requires so,
38596524a52SOrion Hodson 		 * then scale to 0..max-1 to compute the value to
38696524a52SOrion Hodson 		 * write into the codec, and scale back to 0..100
38796524a52SOrion Hodson 		 * for the return value.
38896524a52SOrion Hodson 		 */
389987e5972SCameron Grant 		if (e->reg > 0) {
390987e5972SCameron Grant 			left = 100 - left;
391987e5972SCameron Grant 			right = 100 - right;
392987e5972SCameron Grant 		}
393987e5972SCameron Grant 
394987e5972SCameron Grant 		left = (left * max) / 100;
395987e5972SCameron Grant 		right = (right * max) / 100;
396987e5972SCameron Grant 
397987e5972SCameron Grant 		val = (left << 8) | right;
398987e5972SCameron Grant 
399987e5972SCameron Grant 		left = (left * 100) / max;
400987e5972SCameron Grant 		right = (right * 100) / max;
401987e5972SCameron Grant 
402987e5972SCameron Grant 		if (e->reg > 0) {
403987e5972SCameron Grant 			left = 100 - left;
404987e5972SCameron Grant 			right = 100 - right;
405987e5972SCameron Grant 		}
406987e5972SCameron Grant 
40796524a52SOrion Hodson 		/*
40896524a52SOrion Hodson 		 * For mono controls, trim val and mask, also taking
40996524a52SOrion Hodson 		 * care of e->ofs (offset of control field).
41096524a52SOrion Hodson 		 */
41196524a52SOrion Hodson 		if (e->ofs) {
412987e5972SCameron Grant 			val &= max;
413987e5972SCameron Grant 			val <<= e->ofs;
41496524a52SOrion Hodson 			mask = (max << e->ofs);
41596524a52SOrion Hodson 		}
41696524a52SOrion Hodson 
41796524a52SOrion Hodson 		/*
41896524a52SOrion Hodson 		 * If we have a mute bit, add it to the mask and
41996524a52SOrion Hodson 		 * update val and set mute if both channels require a
42096524a52SOrion Hodson 		 * zero volume.
42196524a52SOrion Hodson 		 */
42296524a52SOrion Hodson 		if (e->mute == 1) {
42396524a52SOrion Hodson 			mask |= AC97_MUTE;
42496524a52SOrion Hodson 			if (left == 0 && right == 0)
42596524a52SOrion Hodson 				val = AC97_MUTE;
42696524a52SOrion Hodson 		}
42796524a52SOrion Hodson 
42896524a52SOrion Hodson 		/*
42996524a52SOrion Hodson 		 * If the mask bit is set, do not alter the other bits.
43096524a52SOrion Hodson 		 */
43196524a52SOrion Hodson 		snd_mtxlock(codec->lock);
432987e5972SCameron Grant 		if (e->mask) {
433f9eb1409SOrion Hodson 			int cur = ac97_rdcd(codec, e->reg);
43496524a52SOrion Hodson 			val |= cur & ~(mask);
435987e5972SCameron Grant 		}
436f9eb1409SOrion Hodson 		ac97_wrcd(codec, reg, val);
43766ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
438987e5972SCameron Grant 		return left | (right << 8);
439341f16ccSCameron Grant 	} else {
440341f16ccSCameron Grant 		/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
44139004e69SCameron Grant 		return -1;
442987e5972SCameron Grant 	}
443341f16ccSCameron Grant }
444987e5972SCameron Grant 
445108082c4SOrion Hodson static void
446108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec)
447108082c4SOrion Hodson {
448cfd5696dSOrion Hodson 	int keep_ogain;
449cfd5696dSOrion Hodson 
450a52604cfSOrion Hodson 	/*
451cfd5696dSOrion Hodson 	 * By default, The ac97 aux_out register (0x04) corresponds to OSS's
452cfd5696dSOrion Hodson 	 * OGAIN setting.
453a52604cfSOrion Hodson 	 *
454cfd5696dSOrion Hodson 	 * We first check whether aux_out is a valid register.  If not
455cfd5696dSOrion Hodson 	 * we may not want to keep ogain.
456a52604cfSOrion Hodson 	 */
457cfd5696dSOrion Hodson 	keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000;
458a52604cfSOrion Hodson 
459a52604cfSOrion Hodson 	/*
460a52604cfSOrion Hodson 	 * Determine what AUX_OUT really means, it can be:
461108082c4SOrion Hodson 	 *
462108082c4SOrion Hodson 	 * 1. Headphone out.
463108082c4SOrion Hodson 	 * 2. 4-Channel Out
464108082c4SOrion Hodson 	 * 3. True line level out (effectively master volume).
465108082c4SOrion Hodson 	 *
466108082c4SOrion Hodson 	 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
467108082c4SOrion Hodson 	 */
468a52604cfSOrion Hodson 	if (codec->extcaps & AC97_EXTCAP_SDAC &&
469f9eb1409SOrion Hodson 	    ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
470cfd5696dSOrion Hodson 		codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND;
471cfd5696dSOrion Hodson 		keep_ogain = 1;
472cfd5696dSOrion Hodson 	}
473cfd5696dSOrion Hodson 
474cfd5696dSOrion Hodson 	if (keep_ogain == 0) {
475cfd5696dSOrion Hodson 		bzero(&codec->mix[SOUND_MIXER_OGAIN],
476cfd5696dSOrion Hodson 		      sizeof(codec->mix[SOUND_MIXER_OGAIN]));
477108082c4SOrion Hodson 	}
478a52604cfSOrion Hodson }
479a52604cfSOrion Hodson 
480a52604cfSOrion Hodson static void
481a52604cfSOrion Hodson ac97_fix_tone(struct ac97_info *codec)
482a52604cfSOrion Hodson {
483a52604cfSOrion Hodson 	/* Hide treble and bass if they don't exist */
484a52604cfSOrion Hodson 	if ((codec->caps & AC97_CAP_TONE) == 0) {
485a52604cfSOrion Hodson 		bzero(&codec->mix[SOUND_MIXER_BASS],
486a52604cfSOrion Hodson 		      sizeof(codec->mix[SOUND_MIXER_BASS]));
487a52604cfSOrion Hodson 		bzero(&codec->mix[SOUND_MIXER_TREBLE],
488a52604cfSOrion Hodson 		      sizeof(codec->mix[SOUND_MIXER_TREBLE]));
489a52604cfSOrion Hodson 	}
490108082c4SOrion Hodson }
491108082c4SOrion Hodson 
49219921b23SOrion Hodson static const char*
49319921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
49419921b23SOrion Hodson {
49519921b23SOrion Hodson 	if (cname == NULL) {
49619921b23SOrion Hodson 		sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
49719921b23SOrion Hodson 		return buf;
49819921b23SOrion Hodson 	}
49919921b23SOrion Hodson 
50019921b23SOrion Hodson 	if (vname == NULL) vname = "Unknown";
50119921b23SOrion Hodson 
50219921b23SOrion Hodson 	if (bootverbose) {
50319921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
50419921b23SOrion Hodson 	} else {
50519921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec", vname, cname);
50619921b23SOrion Hodson 	}
50719921b23SOrion Hodson 	return buf;
50819921b23SOrion Hodson }
50919921b23SOrion Hodson 
510987e5972SCameron Grant static unsigned
51139004e69SCameron Grant ac97_initmixer(struct ac97_info *codec)
512987e5972SCameron Grant {
513f9eb1409SOrion Hodson 	ac97_patch codec_patch;
51419921b23SOrion Hodson 	const char *cname, *vname;
51519921b23SOrion Hodson 	char desc[80];
51619921b23SOrion Hodson 	u_int8_t model, step;
517341f16ccSCameron Grant 	unsigned i, j, k, old;
518987e5972SCameron Grant 	u_int32_t id;
519987e5972SCameron Grant 
52066ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
5210f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
522cd2c103aSCameron Grant 	if (codec->count == 0) {
52304553e63SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
52466ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
52504553e63SCameron Grant 		return ENODEV;
52604553e63SCameron Grant 	}
5279ec437a3SCameron Grant 
528f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
529c6d4b83aSOrion Hodson 	ac97_reset(codec);
530f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
531987e5972SCameron Grant 
532f9eb1409SOrion Hodson 	i = ac97_rdcd(codec, AC97_REG_RESET);
533987e5972SCameron Grant 	codec->caps = i & 0x03ff;
534987e5972SCameron Grant 	codec->se =  (i & 0x7c00) >> 10;
535987e5972SCameron Grant 
536f9eb1409SOrion Hodson 	id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
537e620d959SCameron Grant 	if (id == 0 || id == 0xffffffff) {
538e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
53966ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
540e620d959SCameron Grant 		return ENODEV;
541e620d959SCameron Grant 	}
5426b4b88f7SCameron Grant 
54319921b23SOrion Hodson 	codec->id = id;
544cd2c103aSCameron Grant 	codec->noext = 0;
545f9eb1409SOrion Hodson 	codec_patch = NULL;
54619921b23SOrion Hodson 
54719921b23SOrion Hodson 	cname = NULL;
54819921b23SOrion Hodson 	model = step = 0;
549cd2c103aSCameron Grant 	for (i = 0; ac97codecid[i].id; i++) {
55019921b23SOrion Hodson 		u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
55119921b23SOrion Hodson 		if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
552cd2c103aSCameron Grant 			codec->noext = ac97codecid[i].noext;
553f9eb1409SOrion Hodson 			codec_patch = ac97codecid[i].patch;
55419921b23SOrion Hodson 			cname = ac97codecid[i].name;
55519921b23SOrion Hodson 			model = (id & modelmask) & 0xff;
55619921b23SOrion Hodson 			step = (id & ~modelmask) & 0xff;
55719921b23SOrion Hodson 			break;
55819921b23SOrion Hodson 		}
55919921b23SOrion Hodson 	}
56019921b23SOrion Hodson 
56119921b23SOrion Hodson 	vname = NULL;
56219921b23SOrion Hodson 	for (i = 0; ac97vendorid[i].id; i++) {
56319921b23SOrion Hodson 		if (ac97vendorid[i].id == (id & 0xffffff00)) {
56419921b23SOrion Hodson 			vname = ac97vendorid[i].name;
56519921b23SOrion Hodson 			break;
566cd2c103aSCameron Grant 		}
567cd2c103aSCameron Grant 	}
5686b4b88f7SCameron Grant 
569cd2c103aSCameron Grant 	codec->extcaps = 0;
570cd2c103aSCameron Grant 	codec->extid = 0;
571cd2c103aSCameron Grant 	codec->extstat = 0;
5726a6ee5bbSCameron Grant 	if (!codec->noext) {
573f9eb1409SOrion Hodson 		i = ac97_rdcd(codec, AC97_REGEXT_ID);
5746a6ee5bbSCameron Grant 		if (i != 0xffff) {
57539004e69SCameron Grant 			codec->extcaps = i & 0x3fff;
57639004e69SCameron Grant 			codec->extid =  (i & 0xc000) >> 14;
577f9eb1409SOrion Hodson 			codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
5786b4b88f7SCameron Grant 		}
5796a6ee5bbSCameron Grant 	}
580987e5972SCameron Grant 
581341f16ccSCameron Grant 	for (i = 0; i < 32; i++) {
582108082c4SOrion Hodson 		codec->mix[i] = ac97mixtable_default[i];
583108082c4SOrion Hodson 	}
584108082c4SOrion Hodson 	ac97_fix_auxout(codec);
585a52604cfSOrion Hodson 	ac97_fix_tone(codec);
586f9eb1409SOrion Hodson 	if (codec_patch)
587f9eb1409SOrion Hodson 		codec_patch(codec);
588108082c4SOrion Hodson 
589108082c4SOrion Hodson 	for (i = 0; i < 32; i++) {
59033c878f0SCameron Grant 		k = codec->noext? codec->mix[i].enable : 1;
59133c878f0SCameron Grant 		if (k && (codec->mix[i].reg > 0)) {
592f9eb1409SOrion Hodson 			old = ac97_rdcd(codec, codec->mix[i].reg);
593f9eb1409SOrion Hodson 			ac97_wrcd(codec, codec->mix[i].reg, 0x3f);
594f9eb1409SOrion Hodson 			j = ac97_rdcd(codec, codec->mix[i].reg);
595f9eb1409SOrion Hodson 			ac97_wrcd(codec, codec->mix[i].reg, old);
5966a6ee5bbSCameron Grant 			codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
597341f16ccSCameron Grant 			for (k = 1; j & (1 << k); k++);
598341f16ccSCameron Grant 			codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
599341f16ccSCameron Grant 		}
600341f16ccSCameron Grant 		/* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
601341f16ccSCameron Grant 	}
602987e5972SCameron Grant 
60319921b23SOrion Hodson 	device_printf(codec->dev, "<%s>\n",
60419921b23SOrion Hodson 		      ac97_hw_desc(codec->id, vname, cname, desc));
605a825c6e5SOrion Hodson 
606987e5972SCameron Grant 	if (bootverbose) {
60719921b23SOrion Hodson 		device_printf(codec->dev, "Codec features ");
60839004e69SCameron Grant 		for (i = j = 0; i < 10; i++)
60939004e69SCameron Grant 			if (codec->caps & (1 << i))
61039004e69SCameron Grant 				printf("%s%s", j++? ", " : "", ac97feature[i]);
61139004e69SCameron Grant 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
612987e5972SCameron Grant 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
61339004e69SCameron Grant 
61439004e69SCameron Grant 		if (codec->extcaps != 0 || codec->extid) {
61519921b23SOrion Hodson 			device_printf(codec->dev, "%s codec",
61619921b23SOrion Hodson 				      codec->extid? "Secondary" : "Primary");
61739004e69SCameron Grant 			if (codec->extcaps)
61839004e69SCameron Grant 				printf(" extended features ");
61939004e69SCameron Grant 			for (i = j = 0; i < 14; i++)
62039004e69SCameron Grant 				if (codec->extcaps & (1 << i))
62139004e69SCameron Grant 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
62239004e69SCameron Grant 			printf("\n");
62339004e69SCameron Grant 		}
624987e5972SCameron Grant 	}
625987e5972SCameron Grant 
626f9eb1409SOrion Hodson 	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
62703a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
62866ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
629987e5972SCameron Grant 	return 0;
630987e5972SCameron Grant }
631987e5972SCameron Grant 
6329ec437a3SCameron Grant static unsigned
6339ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec)
6349ec437a3SCameron Grant {
63566ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
6360f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
6379ec437a3SCameron Grant 	if (codec->count == 0) {
6389ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
63966ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
6409ec437a3SCameron Grant 		return ENODEV;
6419ec437a3SCameron Grant 	}
6429ec437a3SCameron Grant 
643f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
644c6d4b83aSOrion Hodson 	ac97_reset(codec);
645f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
6469ec437a3SCameron Grant 
6479ec437a3SCameron Grant 	if (!codec->noext) {
648f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
649f9eb1409SOrion Hodson 		if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
650b60e55dbSGuido van Rooij 		    != codec->extstat)
6519ec437a3SCameron Grant 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
652b60e55dbSGuido van Rooij 				      codec->extstat,
653f9eb1409SOrion Hodson 				      ac97_rdcd(codec, AC97_REGEXT_STAT) &
654b60e55dbSGuido van Rooij 				      AC97_EXTCAPS);
6559ec437a3SCameron Grant 	}
6569ec437a3SCameron Grant 
657f9eb1409SOrion Hodson 	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
6589ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
65966ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
6609ec437a3SCameron Grant 	return 0;
6619ec437a3SCameron Grant }
6629ec437a3SCameron Grant 
663987e5972SCameron Grant struct ac97_info *
6640f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
665987e5972SCameron Grant {
666987e5972SCameron Grant 	struct ac97_info *codec;
667987e5972SCameron Grant 
6680f55ac6cSCameron Grant 	codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
6690f55ac6cSCameron Grant 	if (codec == NULL)
6700f55ac6cSCameron Grant 		return NULL;
6710f55ac6cSCameron Grant 
67266ef8af5SCameron Grant 	snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
673489c22ebSJohn Baldwin 	codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
674a163d034SWarner Losh 	codec->methods = kobj_create(cls, M_AC97, M_WAITOK);
6750f55ac6cSCameron Grant 	if (codec->methods == NULL) {
67666ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
67766ef8af5SCameron Grant 		snd_mtxfree(codec->lock);
6780f55ac6cSCameron Grant 		free(codec, M_AC97);
6790f55ac6cSCameron Grant 		return NULL;
680987e5972SCameron Grant 	}
6810f55ac6cSCameron Grant 
6820f55ac6cSCameron Grant 	codec->dev = dev;
6830f55ac6cSCameron Grant 	codec->devinfo = devinfo;
68479bb7d52SCameron Grant 	codec->flags = 0;
685987e5972SCameron Grant 	return codec;
686987e5972SCameron Grant }
687987e5972SCameron Grant 
68833dbf14aSCameron Grant void
68933dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec)
69033dbf14aSCameron Grant {
69166ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
6920f55ac6cSCameron Grant 	if (codec->methods != NULL)
6930f55ac6cSCameron Grant 		kobj_delete(codec->methods, M_AC97);
69466ef8af5SCameron Grant 	snd_mtxfree(codec->lock);
6950f55ac6cSCameron Grant 	free(codec, M_AC97);
69633dbf14aSCameron Grant }
69733dbf14aSCameron Grant 
69879bb7d52SCameron Grant void
69979bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val)
70079bb7d52SCameron Grant {
70179bb7d52SCameron Grant 	codec->flags = val;
70279bb7d52SCameron Grant }
70379bb7d52SCameron Grant 
70479bb7d52SCameron Grant u_int32_t
70579bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec)
70679bb7d52SCameron Grant {
70779bb7d52SCameron Grant 	return codec->flags;
70879bb7d52SCameron Grant }
70979bb7d52SCameron Grant 
7100f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
7110f55ac6cSCameron Grant 
712987e5972SCameron Grant static int
71366ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m)
714987e5972SCameron Grant {
715987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
716341f16ccSCameron Grant 	u_int32_t i, mask;
717341f16ccSCameron Grant 
71839004e69SCameron Grant 	if (codec == NULL)
71939004e69SCameron Grant 		return -1;
720341f16ccSCameron Grant 
721e620d959SCameron Grant 	if (ac97_initmixer(codec))
722e620d959SCameron Grant 		return -1;
723341f16ccSCameron Grant 
724341f16ccSCameron Grant 	mask = 0;
725341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
726341f16ccSCameron Grant 		mask |= codec->mix[i].enable? 1 << i : 0;
727341f16ccSCameron Grant 	mix_setdevs(m, mask);
728341f16ccSCameron Grant 
729341f16ccSCameron Grant 	mask = 0;
730341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
731341f16ccSCameron Grant 		mask |= codec->mix[i].recidx? 1 << i : 0;
732341f16ccSCameron Grant 	mix_setrecdevs(m, mask);
733987e5972SCameron Grant 	return 0;
734987e5972SCameron Grant }
735987e5972SCameron Grant 
736987e5972SCameron Grant static int
73766ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m)
73833dbf14aSCameron Grant {
73933dbf14aSCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
740341f16ccSCameron Grant 
74133dbf14aSCameron Grant 	if (codec == NULL)
74233dbf14aSCameron Grant 		return -1;
74333dbf14aSCameron Grant 	/*
74433dbf14aSCameron Grant 	if (ac97_uninitmixer(codec))
74533dbf14aSCameron Grant 		return -1;
74633dbf14aSCameron Grant 	*/
74733dbf14aSCameron Grant 	ac97_destroy(codec);
74833dbf14aSCameron Grant 	return 0;
74933dbf14aSCameron Grant }
75033dbf14aSCameron Grant 
75133dbf14aSCameron Grant static int
75266ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m)
7539ec437a3SCameron Grant {
7549ec437a3SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
7559ec437a3SCameron Grant 
7569ec437a3SCameron Grant 	if (codec == NULL)
7579ec437a3SCameron Grant 		return -1;
7589ec437a3SCameron Grant 	return ac97_reinitmixer(codec);
7599ec437a3SCameron Grant }
7609ec437a3SCameron Grant 
7619ec437a3SCameron Grant static int
76266ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
763987e5972SCameron Grant {
764987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
765341f16ccSCameron Grant 
76639004e69SCameron Grant 	if (codec == NULL)
76739004e69SCameron Grant 		return -1;
768987e5972SCameron Grant 	return ac97_setmixer(codec, dev, left, right);
769987e5972SCameron Grant }
770987e5972SCameron Grant 
771987e5972SCameron Grant static int
77266ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
773987e5972SCameron Grant {
774987e5972SCameron Grant 	int i;
775987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
776341f16ccSCameron Grant 
77739004e69SCameron Grant 	if (codec == NULL)
77839004e69SCameron Grant 		return -1;
779987e5972SCameron Grant 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
78039004e69SCameron Grant 		if ((src & (1 << i)) != 0)
78139004e69SCameron Grant 			break;
782987e5972SCameron Grant 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
783987e5972SCameron Grant }
784987e5972SCameron Grant 
7850f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = {
7860f55ac6cSCameron Grant     	KOBJMETHOD(mixer_init,		ac97mix_init),
7870f55ac6cSCameron Grant     	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
7880f55ac6cSCameron Grant     	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
7890f55ac6cSCameron Grant     	KOBJMETHOD(mixer_set,		ac97mix_set),
7900f55ac6cSCameron Grant     	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
7910f55ac6cSCameron Grant 	{ 0, 0 }
792987e5972SCameron Grant };
7930f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer);
7940f55ac6cSCameron Grant 
7950f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
7960f55ac6cSCameron Grant 
7970f55ac6cSCameron Grant kobj_class_t
7980f55ac6cSCameron Grant ac97_getmixerclass(void)
7990f55ac6cSCameron Grant {
8000f55ac6cSCameron Grant 	return &ac97mixer_class;
8010f55ac6cSCameron Grant }
8020f55ac6cSCameron Grant 
803987e5972SCameron Grant 
804