xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision c5cb8d604cc3055510e6aa169af8ded855151e1e)
1098ca2bdSWarner Losh /*-
23f225978SCameron Grant  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
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" },
1053c6b655dSMathew Kanner 	{ 0x44543000, "Diamond Technology" },
10661a0da1dSOrion Hodson 	{ 0x454d4300, "eMicro" },
10719921b23SOrion Hodson 	{ 0x45838300, "ESS Technology" },
1083c6b655dSMathew Kanner 	{ 0x48525300, "Intersil" },
10919921b23SOrion Hodson 	{ 0x49434500, "ICEnsemble" },
1103c6b655dSMathew Kanner 	{ 0x49544500, "ITE, Inc." },
11119921b23SOrion Hodson 	{ 0x4e534300, "National Semiconductor" },
11219921b23SOrion Hodson 	{ 0x50534300, "Philips Semiconductor" },
11319921b23SOrion Hodson 	{ 0x83847600, "SigmaTel" },
1143c6b655dSMathew Kanner 	{ 0x53494c00, "Silicon Laboratories" },
11519921b23SOrion Hodson 	{ 0x54524100, "TriTech" },
1163c6b655dSMathew Kanner 	{ 0x54584e00, "Texas Instruments" },
11719921b23SOrion Hodson 	{ 0x56494100, "VIA Technologies" },
1183c6b655dSMathew Kanner 	{ 0x57454300, "Winbond" },
11919921b23SOrion Hodson 	{ 0x574d4c00, "Wolfson" },
12019921b23SOrion Hodson 	{ 0x594d4800, "Yamaha" },
1216f0182bdSOrion Hodson 	{ 0x01408300, "Creative" },
12219921b23SOrion Hodson 	{ 0x00000000, NULL }
12319921b23SOrion Hodson };
12419921b23SOrion Hodson 
125987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = {
12619921b23SOrion Hodson 	{ 0x41445303, 0x00, 0, "AD1819",	0 },
12719921b23SOrion Hodson 	{ 0x41445340, 0x00, 0, "AD1881",	0 },
12819921b23SOrion Hodson 	{ 0x41445348, 0x00, 0, "AD1881A",	0 },
12919921b23SOrion Hodson 	{ 0x41445360, 0x00, 0, "AD1885",	0 },
13019921b23SOrion Hodson 	{ 0x41445361, 0x00, 0, "AD1886", 	ad1886_patch },
131ba548c64SOrion Hodson 	{ 0x41445362, 0x00, 0, "AD1887", 	0 },
132ba548c64SOrion Hodson 	{ 0x41445363, 0x00, 0, "AD1886A", 	0 },
133c5cb8d60SScott Long 	{ 0x41445368, 0x00, 0, "AD1888", 	ad198x_patch },
134a52604cfSOrion Hodson 	{ 0x41445370, 0x00, 0, "AD1980",	ad198x_patch },
135ba548c64SOrion Hodson 	{ 0x41445372, 0x00, 0, "AD1981A",	0 },
136ba548c64SOrion Hodson 	{ 0x41445374, 0x00, 0, "AD1981B",	0 },
137a52604cfSOrion Hodson 	{ 0x41445375, 0x00, 0, "AD1985",	ad198x_patch },
13819921b23SOrion Hodson 	{ 0x414b4d00, 0x00, 1, "AK4540", 	0 },
13919921b23SOrion Hodson 	{ 0x414b4d01, 0x00, 1, "AK4542", 	0 },
14019921b23SOrion Hodson 	{ 0x414b4d02, 0x00, 1, "AK4543", 	0 },
1413c6b655dSMathew Kanner 	{ 0x414b4d06, 0x00, 0, "AK4544A",	0 },
1423c6b655dSMathew Kanner 	{ 0x454b4d07, 0x00, 0, "AK4545",	0 },
14319921b23SOrion Hodson 	{ 0x414c4320, 0x0f, 0, "ALC100",	0 },
1449963235aSOrion Hodson 	{ 0x414c4730, 0x0f, 0, "ALC101",	0 },
14519921b23SOrion Hodson 	{ 0x414c4710, 0x0f, 0, "ALC200", 	0 },
14619921b23SOrion Hodson 	{ 0x414c4740, 0x0f, 0, "ALC202", 	0 },
14719921b23SOrion Hodson 	{ 0x414c4720, 0x0f, 0, "ALC650", 	0 },
1485197cdc1SDag-Erling Smørgrav 	{ 0x414c4760, 0x0f, 0, "ALC655",	0 },
14994ed763dSJun Kuriyama 	{ 0x414c4780, 0x0f, 0, "ALC658",	0 },
1503c6b655dSMathew Kanner 	{ 0x414c4790, 0x0f, 0, "ALC850",	0 },
15119921b23SOrion Hodson 	{ 0x43525900, 0x07, 0, "CS4297", 	0 },
15219921b23SOrion Hodson 	{ 0x43525910, 0x07, 0, "CS4297A", 	0 },
15319921b23SOrion Hodson 	{ 0x43525920, 0x07, 0, "CS4294/98",	0 },
154e5728f83SMIHIRA Sanpei Yoshiro 	{ 0x4352592d, 0x07, 0, "CS4294",	0 },
15519921b23SOrion Hodson 	{ 0x43525930, 0x07, 0, "CS4299",	0 },
15619921b23SOrion Hodson 	{ 0x43525940, 0x07, 0, "CS4201",	0 },
157b2a0f525SOrion Hodson 	{ 0x43525958, 0x07, 0, "CS4205",	0 },
15819921b23SOrion Hodson 	{ 0x43525960, 0x07, 0, "CS4291A",	0 },
15919921b23SOrion Hodson 	{ 0x434d4961, 0x00, 0, "CMI9739",	0 },
16019921b23SOrion Hodson 	{ 0x434d4941, 0x00, 0, "CMI9738",	0 },
1613c6b655dSMathew Kanner 	{ 0x43585421, 0x00, 0, "HSD11246",	0 },
1623c6b655dSMathew Kanner 	{ 0x43585428, 0x07, 0, "CX20468",	0 },
1633c6b655dSMathew Kanner 	{ 0x44543000, 0x00, 0, "DT0398",	0 },
16461a0da1dSOrion Hodson 	{ 0x454d4323, 0x00, 0, "EM28023",	0 },
16561a0da1dSOrion Hodson 	{ 0x454d4328, 0x00, 0, "EM28028",	0 },
16619921b23SOrion Hodson 	{ 0x45838308, 0x00, 0, "ES1988",	0 }, /* Formerly ES1921(?) */
1673c6b655dSMathew Kanner 	{ 0x48525300, 0x00, 0, "HMP9701",	0 },
16819921b23SOrion Hodson 	{ 0x49434501, 0x00, 0, "ICE1230",	0 },
16919921b23SOrion Hodson 	{ 0x49434511, 0x00, 0, "ICE1232",	0 },
17019921b23SOrion Hodson 	{ 0x49434514, 0x00, 0, "ICE1232A",	0 },
17149fd6905SOrion Hodson 	{ 0x49434551, 0x03, 0, "VT1616",	0 }, /* Via badged ICE */
1723c6b655dSMathew Kanner 	{ 0x49544520, 0x00, 0, "ITE2226E",	0 },
1733c6b655dSMathew Kanner 	{ 0x49544560, 0x07, 0, "ITE2646E",	0 }, /* XXX: patch needed */
17419921b23SOrion Hodson 	{ 0x4e534340, 0x00, 0, "LM4540",	0 }, /* Spec blank on revid */
17519921b23SOrion Hodson 	{ 0x4e534343, 0x00, 0, "LM4543",	0 }, /* Ditto */
17619921b23SOrion Hodson 	{ 0x4e534346, 0x00, 0, "LM4546A",	0 },
17719921b23SOrion Hodson 	{ 0x4e534348, 0x00, 0, "LM4548A",	0 },
1783c6b655dSMathew Kanner 	{ 0x4e534331, 0x00, 0, "LM4549",	0 },
17919921b23SOrion Hodson 	{ 0x4e534349, 0x00, 0, "LM4549A",	0 },
18019921b23SOrion Hodson 	{ 0x4e534350, 0x00, 0, "LM4550",	0 },
18119921b23SOrion Hodson 	{ 0x50534301, 0x00, 0, "UCB1510",	0 },
18219921b23SOrion Hodson 	{ 0x50534304, 0x00, 0, "UCB1400",	0 },
18319921b23SOrion Hodson 	{ 0x83847600, 0x00, 0, "STAC9700/83/84",	0 },
18419921b23SOrion Hodson 	{ 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
18519921b23SOrion Hodson 	{ 0x83847605, 0x00, 0, "STAC9704",	0 },
18619921b23SOrion Hodson 	{ 0x83847608, 0x00, 0, "STAC9708/11",	0 },
18719921b23SOrion Hodson 	{ 0x83847609, 0x00, 0, "STAC9721/23",	0 },
18819921b23SOrion Hodson 	{ 0x83847644, 0x00, 0, "STAC9744/45",	0 },
18919921b23SOrion Hodson 	{ 0x83847650, 0x00, 0, "STAC9750/51",	0 },
19019921b23SOrion Hodson 	{ 0x83847652, 0x00, 0, "STAC9752/53",	0 },
19119921b23SOrion Hodson 	{ 0x83847656, 0x00, 0, "STAC9756/57",	0 },
19219921b23SOrion Hodson 	{ 0x83847658, 0x00, 0, "STAC9758/59",	0 },
19319921b23SOrion Hodson 	{ 0x83847660, 0x00, 0, "STAC9760/61",	0 }, /* Extrapolated */
19419921b23SOrion Hodson 	{ 0x83847662, 0x00, 0, "STAC9762/63",	0 }, /* Extrapolated */
19519921b23SOrion Hodson 	{ 0x53494c22, 0x00, 0, "Si3036",	0 },
19619921b23SOrion Hodson 	{ 0x53494c23, 0x00, 0, "Si3038",	0 },
19719921b23SOrion Hodson 	{ 0x54524103, 0x00, 0, "TR28023",	0 }, /* Extrapolated */
19819921b23SOrion Hodson 	{ 0x54524106, 0x00, 0, "TR28026",	0 },
19919921b23SOrion Hodson 	{ 0x54524108, 0x00, 0, "TR28028",	0 },
20019921b23SOrion Hodson 	{ 0x54524123, 0x00, 0, "TR28602",	0 },
2013c6b655dSMathew Kanner 	{ 0x54524e03, 0x07, 0, "TLV320AIC27",	0 },
2023c6b655dSMathew Kanner 	{ 0x54584e20, 0x00, 0, "TLC320AD90",	0 },
20319921b23SOrion Hodson 	{ 0x56494161, 0x00, 0, "VIA1612A",      0 },
20419921b23SOrion Hodson 	{ 0x574d4c00, 0x00, 0, "WM9701A",	0 },
20519921b23SOrion Hodson 	{ 0x574d4c03, 0x00, 0, "WM9703/4/7/8",	0 },
20619921b23SOrion Hodson 	{ 0x574d4c04, 0x00, 0, "WM9704Q",	0 },
20719921b23SOrion Hodson 	{ 0x574d4c05, 0x00, 0, "WM9705/10",	0 },
2083c6b655dSMathew Kanner 	{ 0x574d4d09, 0x00, 0, "WM9709",	0 },
2093c6b655dSMathew Kanner 	{ 0x574d4c12, 0x00, 0, "WM9711/12",	0 }, /* XXX: patch needed */
2103c6b655dSMathew Kanner 	{ 0x57454301, 0x00, 0, "W83971D",	0 },
21119921b23SOrion Hodson 	{ 0x594d4800, 0x00, 0, "YMF743",	0 },
21219921b23SOrion Hodson 	{ 0x594d4802, 0x00, 0, "YMF752",	0 },
21319921b23SOrion Hodson 	{ 0x594d4803, 0x00, 0, "YMF753",	0 },
2146f0182bdSOrion Hodson 	{ 0x01408384, 0x00, 0, "EV1938",	0 },
21519921b23SOrion Hodson 	{ 0, 0, 0, NULL, 0 }
216987e5972SCameron Grant };
217987e5972SCameron Grant 
218987e5972SCameron Grant static char *ac97enhancement[] = {
21904553e63SCameron Grant 	"no 3D Stereo Enhancement",
220987e5972SCameron Grant 	"Analog Devices Phat Stereo",
221987e5972SCameron Grant 	"Creative Stereo Enhancement",
222987e5972SCameron Grant 	"National Semi 3D Stereo Enhancement",
223987e5972SCameron Grant 	"Yamaha Ymersion",
224987e5972SCameron Grant 	"BBE 3D Stereo Enhancement",
225987e5972SCameron Grant 	"Crystal Semi 3D Stereo Enhancement",
226987e5972SCameron Grant 	"Qsound QXpander",
227987e5972SCameron Grant 	"Spatializer 3D Stereo Enhancement",
228987e5972SCameron Grant 	"SRS 3D Stereo Enhancement",
229987e5972SCameron Grant 	"Platform Tech 3D Stereo Enhancement",
230987e5972SCameron Grant 	"AKM 3D Audio",
231987e5972SCameron Grant 	"Aureal Stereo Enhancement",
232987e5972SCameron Grant 	"Aztech 3D Enhancement",
233987e5972SCameron Grant 	"Binaura 3D Audio Enhancement",
234987e5972SCameron Grant 	"ESS Technology Stereo Enhancement",
235987e5972SCameron Grant 	"Harman International VMAx",
236987e5972SCameron Grant 	"Nvidea 3D Stereo Enhancement",
237987e5972SCameron Grant 	"Philips Incredible Sound",
238987e5972SCameron Grant 	"Texas Instruments 3D Stereo Enhancement",
239987e5972SCameron Grant 	"VLSI Technology 3D Stereo Enhancement",
240987e5972SCameron Grant 	"TriTech 3D Stereo Enhancement",
241987e5972SCameron Grant 	"Realtek 3D Stereo Enhancement",
242987e5972SCameron Grant 	"Samsung 3D Stereo Enhancement",
243987e5972SCameron Grant 	"Wolfson Microelectronics 3D Enhancement",
244987e5972SCameron Grant 	"Delta Integration 3D Enhancement",
245987e5972SCameron Grant 	"SigmaTel 3D Enhancement",
246987e5972SCameron Grant 	"Reserved 27",
247987e5972SCameron Grant 	"Rockwell 3D Stereo Enhancement",
248987e5972SCameron Grant 	"Reserved 29",
249987e5972SCameron Grant 	"Reserved 30",
250987e5972SCameron Grant 	"Reserved 31"
251987e5972SCameron Grant };
252987e5972SCameron Grant 
253987e5972SCameron Grant static char *ac97feature[] = {
254987e5972SCameron Grant 	"mic channel",
255987e5972SCameron Grant 	"reserved",
256987e5972SCameron Grant 	"tone",
257987e5972SCameron Grant 	"simulated stereo",
258987e5972SCameron Grant 	"headphone",
259987e5972SCameron Grant 	"bass boost",
260987e5972SCameron Grant 	"18 bit DAC",
261987e5972SCameron Grant 	"20 bit DAC",
262987e5972SCameron Grant 	"18 bit ADC",
263987e5972SCameron Grant 	"20 bit ADC"
264987e5972SCameron Grant };
265987e5972SCameron Grant 
26639004e69SCameron Grant static char *ac97extfeature[] = {
26739004e69SCameron Grant 	"variable rate PCM",
26839004e69SCameron Grant 	"double rate PCM",
26939004e69SCameron Grant 	"reserved 1",
27039004e69SCameron Grant 	"variable rate mic",
27139004e69SCameron Grant 	"reserved 2",
27239004e69SCameron Grant 	"reserved 3",
27339004e69SCameron Grant 	"center DAC",
27439004e69SCameron Grant 	"surround DAC",
27539004e69SCameron Grant 	"LFE DAC",
27639004e69SCameron Grant 	"AMAP",
27739004e69SCameron Grant 	"reserved 4",
27839004e69SCameron Grant 	"reserved 5",
27939004e69SCameron Grant 	"reserved 6",
28039004e69SCameron Grant 	"reserved 7",
28139004e69SCameron Grant };
28239004e69SCameron Grant 
283f9eb1409SOrion Hodson u_int16_t
284f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg)
28539004e69SCameron Grant {
2860f55ac6cSCameron Grant 	return AC97_READ(codec->methods, codec->devinfo, reg);
28739004e69SCameron Grant }
28839004e69SCameron Grant 
289f9eb1409SOrion Hodson void
290f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
29139004e69SCameron Grant {
2920f55ac6cSCameron Grant 	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
29339004e69SCameron Grant }
29439004e69SCameron Grant 
295c6d4b83aSOrion Hodson static void
296c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec)
297c6d4b83aSOrion Hodson {
298c6d4b83aSOrion Hodson 	u_int32_t i, ps;
299f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_RESET, 0);
300c6d4b83aSOrion Hodson 	for (i = 0; i < 500; i++) {
301f9eb1409SOrion Hodson 		ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
302c6d4b83aSOrion Hodson 		if (ps == AC97_POWER_STATUS)
303c6d4b83aSOrion Hodson 			return;
304c6d4b83aSOrion Hodson 		DELAY(1000);
305c6d4b83aSOrion Hodson 	}
306a825c6e5SOrion Hodson 	device_printf(codec->dev, "AC97 reset timed out.\n");
307c6d4b83aSOrion Hodson }
308c6d4b83aSOrion Hodson 
30939004e69SCameron Grant int
31039004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate)
31139004e69SCameron Grant {
31239004e69SCameron Grant 	u_int16_t v;
31339004e69SCameron Grant 
31439004e69SCameron Grant 	switch(which) {
31539004e69SCameron Grant 	case AC97_REGEXT_FDACRATE:
31639004e69SCameron Grant 	case AC97_REGEXT_SDACRATE:
31739004e69SCameron Grant 	case AC97_REGEXT_LDACRATE:
31839004e69SCameron Grant 	case AC97_REGEXT_LADCRATE:
31939004e69SCameron Grant 	case AC97_REGEXT_MADCRATE:
32039004e69SCameron Grant 		break;
32139004e69SCameron Grant 
32239004e69SCameron Grant 	default:
32339004e69SCameron Grant 		return -1;
32439004e69SCameron Grant 	}
32539004e69SCameron Grant 
32666ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
32739004e69SCameron Grant 	if (rate != 0) {
32839004e69SCameron Grant 		v = rate;
32939004e69SCameron Grant 		if (codec->extstat & AC97_EXTCAP_DRA)
33039004e69SCameron Grant 			v >>= 1;
331f9eb1409SOrion Hodson 		ac97_wrcd(codec, which, v);
33239004e69SCameron Grant 	}
333f9eb1409SOrion Hodson 	v = ac97_rdcd(codec, which);
33439004e69SCameron Grant 	if (codec->extstat & AC97_EXTCAP_DRA)
33539004e69SCameron Grant 		v <<= 1;
33666ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
33739004e69SCameron Grant 	return v;
33839004e69SCameron Grant }
33939004e69SCameron Grant 
34039004e69SCameron Grant int
34139004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
34239004e69SCameron Grant {
34339004e69SCameron Grant 	mode &= AC97_EXTCAPS;
344647fbfebSOrion Hodson 	if ((mode & ~codec->extcaps) != 0) {
345647fbfebSOrion Hodson 		device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
346647fbfebSOrion Hodson 			      mode);
34739004e69SCameron Grant 		return -1;
348647fbfebSOrion Hodson 	}
34966ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
350f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
351f9eb1409SOrion Hodson 	codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
35266ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
35339004e69SCameron Grant 	return (mode == codec->extstat)? 0 : -1;
35439004e69SCameron Grant }
35539004e69SCameron Grant 
3569ec437a3SCameron Grant u_int16_t
3579ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec)
3589ec437a3SCameron Grant {
3599ec437a3SCameron Grant 	return codec->extstat;
3609ec437a3SCameron Grant }
3619ec437a3SCameron Grant 
3629ec437a3SCameron Grant u_int16_t
3639ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec)
3649ec437a3SCameron Grant {
3659ec437a3SCameron Grant 	return codec->extcaps;
3669ec437a3SCameron Grant }
3679ec437a3SCameron Grant 
3685d91ad67SCameron Grant u_int16_t
3695d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec)
3705d91ad67SCameron Grant {
3715d91ad67SCameron Grant 	return codec->caps;
3725d91ad67SCameron Grant }
3735d91ad67SCameron Grant 
374987e5972SCameron Grant static int
375987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel)
376987e5972SCameron Grant {
377987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
378341f16ccSCameron Grant 
379987e5972SCameron Grant 	if (e->recidx > 0) {
380987e5972SCameron Grant 		int val = e->recidx - 1;
381987e5972SCameron Grant 		val |= val << 8;
38266ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
383f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REG_RECSEL, val);
38466ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
385987e5972SCameron Grant 		return 0;
38639004e69SCameron Grant 	} else
38739004e69SCameron Grant 		return -1;
388987e5972SCameron Grant }
389987e5972SCameron Grant 
390987e5972SCameron Grant static int
391987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
392987e5972SCameron Grant {
393987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
394341f16ccSCameron Grant 
395341f16ccSCameron Grant 	if (e->reg && e->enable && e->bits) {
39696524a52SOrion Hodson 		int mask, max, val, reg;
39796524a52SOrion Hodson 
39896524a52SOrion Hodson 		reg = (e->reg >= 0) ? e->reg : -e->reg;	/* AC97 register    */
39996524a52SOrion Hodson 		max = (1 << e->bits) - 1;		/* actual range	    */
40096524a52SOrion Hodson 		mask = (max << 8) | max;		/* bits of interest */
401987e5972SCameron Grant 
40239004e69SCameron Grant 		if (!e->stereo)
40339004e69SCameron Grant 			right = left;
40496524a52SOrion Hodson 
40596524a52SOrion Hodson 		/*
40696524a52SOrion Hodson 		 * Invert the range if the polarity requires so,
40796524a52SOrion Hodson 		 * then scale to 0..max-1 to compute the value to
40896524a52SOrion Hodson 		 * write into the codec, and scale back to 0..100
40996524a52SOrion Hodson 		 * for the return value.
41096524a52SOrion Hodson 		 */
411987e5972SCameron Grant 		if (e->reg > 0) {
412987e5972SCameron Grant 			left = 100 - left;
413987e5972SCameron Grant 			right = 100 - right;
414987e5972SCameron Grant 		}
415987e5972SCameron Grant 
416987e5972SCameron Grant 		left = (left * max) / 100;
417987e5972SCameron Grant 		right = (right * max) / 100;
418987e5972SCameron Grant 
419987e5972SCameron Grant 		val = (left << 8) | right;
420987e5972SCameron Grant 
421987e5972SCameron Grant 		left = (left * 100) / max;
422987e5972SCameron Grant 		right = (right * 100) / max;
423987e5972SCameron Grant 
424987e5972SCameron Grant 		if (e->reg > 0) {
425987e5972SCameron Grant 			left = 100 - left;
426987e5972SCameron Grant 			right = 100 - right;
427987e5972SCameron Grant 		}
428987e5972SCameron Grant 
42996524a52SOrion Hodson 		/*
43096524a52SOrion Hodson 		 * For mono controls, trim val and mask, also taking
43196524a52SOrion Hodson 		 * care of e->ofs (offset of control field).
43296524a52SOrion Hodson 		 */
43396524a52SOrion Hodson 		if (e->ofs) {
434987e5972SCameron Grant 			val &= max;
435987e5972SCameron Grant 			val <<= e->ofs;
43696524a52SOrion Hodson 			mask = (max << e->ofs);
43796524a52SOrion Hodson 		}
43896524a52SOrion Hodson 
43996524a52SOrion Hodson 		/*
44096524a52SOrion Hodson 		 * If we have a mute bit, add it to the mask and
44196524a52SOrion Hodson 		 * update val and set mute if both channels require a
44296524a52SOrion Hodson 		 * zero volume.
44396524a52SOrion Hodson 		 */
44496524a52SOrion Hodson 		if (e->mute == 1) {
44596524a52SOrion Hodson 			mask |= AC97_MUTE;
44696524a52SOrion Hodson 			if (left == 0 && right == 0)
44796524a52SOrion Hodson 				val = AC97_MUTE;
44896524a52SOrion Hodson 		}
44996524a52SOrion Hodson 
45096524a52SOrion Hodson 		/*
45196524a52SOrion Hodson 		 * If the mask bit is set, do not alter the other bits.
45296524a52SOrion Hodson 		 */
45396524a52SOrion Hodson 		snd_mtxlock(codec->lock);
454987e5972SCameron Grant 		if (e->mask) {
455f9eb1409SOrion Hodson 			int cur = ac97_rdcd(codec, e->reg);
45696524a52SOrion Hodson 			val |= cur & ~(mask);
457987e5972SCameron Grant 		}
458f9eb1409SOrion Hodson 		ac97_wrcd(codec, reg, val);
45966ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
460987e5972SCameron Grant 		return left | (right << 8);
461341f16ccSCameron Grant 	} else {
462341f16ccSCameron Grant 		/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
46339004e69SCameron Grant 		return -1;
464987e5972SCameron Grant 	}
465341f16ccSCameron Grant }
466987e5972SCameron Grant 
467108082c4SOrion Hodson static void
468108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec)
469108082c4SOrion Hodson {
470cfd5696dSOrion Hodson 	int keep_ogain;
471cfd5696dSOrion Hodson 
472a52604cfSOrion Hodson 	/*
473cfd5696dSOrion Hodson 	 * By default, The ac97 aux_out register (0x04) corresponds to OSS's
474cfd5696dSOrion Hodson 	 * OGAIN setting.
475a52604cfSOrion Hodson 	 *
476cfd5696dSOrion Hodson 	 * We first check whether aux_out is a valid register.  If not
477cfd5696dSOrion Hodson 	 * we may not want to keep ogain.
478a52604cfSOrion Hodson 	 */
479cfd5696dSOrion Hodson 	keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000;
480a52604cfSOrion Hodson 
481a52604cfSOrion Hodson 	/*
482a52604cfSOrion Hodson 	 * Determine what AUX_OUT really means, it can be:
483108082c4SOrion Hodson 	 *
484108082c4SOrion Hodson 	 * 1. Headphone out.
485108082c4SOrion Hodson 	 * 2. 4-Channel Out
486108082c4SOrion Hodson 	 * 3. True line level out (effectively master volume).
487108082c4SOrion Hodson 	 *
488108082c4SOrion Hodson 	 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
489108082c4SOrion Hodson 	 */
490a52604cfSOrion Hodson 	if (codec->extcaps & AC97_EXTCAP_SDAC &&
491f9eb1409SOrion Hodson 	    ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
492cfd5696dSOrion Hodson 		codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND;
493cfd5696dSOrion Hodson 		keep_ogain = 1;
494cfd5696dSOrion Hodson 	}
495cfd5696dSOrion Hodson 
496cfd5696dSOrion Hodson 	if (keep_ogain == 0) {
497cfd5696dSOrion Hodson 		bzero(&codec->mix[SOUND_MIXER_OGAIN],
498cfd5696dSOrion Hodson 		      sizeof(codec->mix[SOUND_MIXER_OGAIN]));
499108082c4SOrion Hodson 	}
500a52604cfSOrion Hodson }
501a52604cfSOrion Hodson 
502a52604cfSOrion Hodson static void
503a52604cfSOrion Hodson ac97_fix_tone(struct ac97_info *codec)
504a52604cfSOrion Hodson {
505a52604cfSOrion Hodson 	/* Hide treble and bass if they don't exist */
506a52604cfSOrion Hodson 	if ((codec->caps & AC97_CAP_TONE) == 0) {
507a52604cfSOrion Hodson 		bzero(&codec->mix[SOUND_MIXER_BASS],
508a52604cfSOrion Hodson 		      sizeof(codec->mix[SOUND_MIXER_BASS]));
509a52604cfSOrion Hodson 		bzero(&codec->mix[SOUND_MIXER_TREBLE],
510a52604cfSOrion Hodson 		      sizeof(codec->mix[SOUND_MIXER_TREBLE]));
511a52604cfSOrion Hodson 	}
512108082c4SOrion Hodson }
513108082c4SOrion Hodson 
51419921b23SOrion Hodson static const char*
51519921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
51619921b23SOrion Hodson {
51719921b23SOrion Hodson 	if (cname == NULL) {
51819921b23SOrion Hodson 		sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
51919921b23SOrion Hodson 		return buf;
52019921b23SOrion Hodson 	}
52119921b23SOrion Hodson 
52219921b23SOrion Hodson 	if (vname == NULL) vname = "Unknown";
52319921b23SOrion Hodson 
52419921b23SOrion Hodson 	if (bootverbose) {
52519921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
52619921b23SOrion Hodson 	} else {
52719921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec", vname, cname);
52819921b23SOrion Hodson 	}
52919921b23SOrion Hodson 	return buf;
53019921b23SOrion Hodson }
53119921b23SOrion Hodson 
532987e5972SCameron Grant static unsigned
53339004e69SCameron Grant ac97_initmixer(struct ac97_info *codec)
534987e5972SCameron Grant {
535f9eb1409SOrion Hodson 	ac97_patch codec_patch;
53619921b23SOrion Hodson 	const char *cname, *vname;
53719921b23SOrion Hodson 	char desc[80];
53819921b23SOrion Hodson 	u_int8_t model, step;
539341f16ccSCameron Grant 	unsigned i, j, k, old;
540987e5972SCameron Grant 	u_int32_t id;
541987e5972SCameron Grant 
54266ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
5430f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
544cd2c103aSCameron Grant 	if (codec->count == 0) {
54504553e63SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
54666ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
54704553e63SCameron Grant 		return ENODEV;
54804553e63SCameron Grant 	}
5499ec437a3SCameron Grant 
550f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
551c6d4b83aSOrion Hodson 	ac97_reset(codec);
552f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
553987e5972SCameron Grant 
554f9eb1409SOrion Hodson 	i = ac97_rdcd(codec, AC97_REG_RESET);
555987e5972SCameron Grant 	codec->caps = i & 0x03ff;
556987e5972SCameron Grant 	codec->se =  (i & 0x7c00) >> 10;
557987e5972SCameron Grant 
558f9eb1409SOrion Hodson 	id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
559e620d959SCameron Grant 	if (id == 0 || id == 0xffffffff) {
560e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
56166ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
562e620d959SCameron Grant 		return ENODEV;
563e620d959SCameron Grant 	}
5646b4b88f7SCameron Grant 
56519921b23SOrion Hodson 	codec->id = id;
566cd2c103aSCameron Grant 	codec->noext = 0;
567f9eb1409SOrion Hodson 	codec_patch = NULL;
56819921b23SOrion Hodson 
56919921b23SOrion Hodson 	cname = NULL;
57019921b23SOrion Hodson 	model = step = 0;
571cd2c103aSCameron Grant 	for (i = 0; ac97codecid[i].id; i++) {
57219921b23SOrion Hodson 		u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
57319921b23SOrion Hodson 		if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
574cd2c103aSCameron Grant 			codec->noext = ac97codecid[i].noext;
575f9eb1409SOrion Hodson 			codec_patch = ac97codecid[i].patch;
57619921b23SOrion Hodson 			cname = ac97codecid[i].name;
57719921b23SOrion Hodson 			model = (id & modelmask) & 0xff;
57819921b23SOrion Hodson 			step = (id & ~modelmask) & 0xff;
57919921b23SOrion Hodson 			break;
58019921b23SOrion Hodson 		}
58119921b23SOrion Hodson 	}
58219921b23SOrion Hodson 
58319921b23SOrion Hodson 	vname = NULL;
58419921b23SOrion Hodson 	for (i = 0; ac97vendorid[i].id; i++) {
58519921b23SOrion Hodson 		if (ac97vendorid[i].id == (id & 0xffffff00)) {
58619921b23SOrion Hodson 			vname = ac97vendorid[i].name;
58719921b23SOrion Hodson 			break;
588cd2c103aSCameron Grant 		}
589cd2c103aSCameron Grant 	}
5906b4b88f7SCameron Grant 
591cd2c103aSCameron Grant 	codec->extcaps = 0;
592cd2c103aSCameron Grant 	codec->extid = 0;
593cd2c103aSCameron Grant 	codec->extstat = 0;
5946a6ee5bbSCameron Grant 	if (!codec->noext) {
595f9eb1409SOrion Hodson 		i = ac97_rdcd(codec, AC97_REGEXT_ID);
5966a6ee5bbSCameron Grant 		if (i != 0xffff) {
59739004e69SCameron Grant 			codec->extcaps = i & 0x3fff;
59839004e69SCameron Grant 			codec->extid =  (i & 0xc000) >> 14;
599f9eb1409SOrion Hodson 			codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
6006b4b88f7SCameron Grant 		}
6016a6ee5bbSCameron Grant 	}
602987e5972SCameron Grant 
603341f16ccSCameron Grant 	for (i = 0; i < 32; i++) {
604108082c4SOrion Hodson 		codec->mix[i] = ac97mixtable_default[i];
605108082c4SOrion Hodson 	}
606108082c4SOrion Hodson 	ac97_fix_auxout(codec);
607a52604cfSOrion Hodson 	ac97_fix_tone(codec);
608f9eb1409SOrion Hodson 	if (codec_patch)
609f9eb1409SOrion Hodson 		codec_patch(codec);
610108082c4SOrion Hodson 
611108082c4SOrion Hodson 	for (i = 0; i < 32; i++) {
61233c878f0SCameron Grant 		k = codec->noext? codec->mix[i].enable : 1;
61333c878f0SCameron Grant 		if (k && (codec->mix[i].reg > 0)) {
614f9eb1409SOrion Hodson 			old = ac97_rdcd(codec, codec->mix[i].reg);
615f9eb1409SOrion Hodson 			ac97_wrcd(codec, codec->mix[i].reg, 0x3f);
616f9eb1409SOrion Hodson 			j = ac97_rdcd(codec, codec->mix[i].reg);
617f9eb1409SOrion Hodson 			ac97_wrcd(codec, codec->mix[i].reg, old);
6186a6ee5bbSCameron Grant 			codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
619341f16ccSCameron Grant 			for (k = 1; j & (1 << k); k++);
620341f16ccSCameron Grant 			codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
621341f16ccSCameron Grant 		}
622341f16ccSCameron Grant 		/* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
623341f16ccSCameron Grant 	}
624987e5972SCameron Grant 
62519921b23SOrion Hodson 	device_printf(codec->dev, "<%s>\n",
62619921b23SOrion Hodson 		      ac97_hw_desc(codec->id, vname, cname, desc));
627a825c6e5SOrion Hodson 
628987e5972SCameron Grant 	if (bootverbose) {
62919921b23SOrion Hodson 		device_printf(codec->dev, "Codec features ");
63039004e69SCameron Grant 		for (i = j = 0; i < 10; i++)
63139004e69SCameron Grant 			if (codec->caps & (1 << i))
63239004e69SCameron Grant 				printf("%s%s", j++? ", " : "", ac97feature[i]);
63339004e69SCameron Grant 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
634987e5972SCameron Grant 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
63539004e69SCameron Grant 
63639004e69SCameron Grant 		if (codec->extcaps != 0 || codec->extid) {
63719921b23SOrion Hodson 			device_printf(codec->dev, "%s codec",
63819921b23SOrion Hodson 				      codec->extid? "Secondary" : "Primary");
63939004e69SCameron Grant 			if (codec->extcaps)
64039004e69SCameron Grant 				printf(" extended features ");
64139004e69SCameron Grant 			for (i = j = 0; i < 14; i++)
64239004e69SCameron Grant 				if (codec->extcaps & (1 << i))
64339004e69SCameron Grant 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
64439004e69SCameron Grant 			printf("\n");
64539004e69SCameron Grant 		}
646987e5972SCameron Grant 	}
647987e5972SCameron Grant 
648f9eb1409SOrion Hodson 	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
64903a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
65066ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
651987e5972SCameron Grant 	return 0;
652987e5972SCameron Grant }
653987e5972SCameron Grant 
6549ec437a3SCameron Grant static unsigned
6559ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec)
6569ec437a3SCameron Grant {
65766ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
6580f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
6599ec437a3SCameron Grant 	if (codec->count == 0) {
6609ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
66166ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
6629ec437a3SCameron Grant 		return ENODEV;
6639ec437a3SCameron Grant 	}
6649ec437a3SCameron Grant 
665f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
666c6d4b83aSOrion Hodson 	ac97_reset(codec);
667f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
6689ec437a3SCameron Grant 
6699ec437a3SCameron Grant 	if (!codec->noext) {
670f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
671f9eb1409SOrion Hodson 		if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
672b60e55dbSGuido van Rooij 		    != codec->extstat)
6739ec437a3SCameron Grant 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
674b60e55dbSGuido van Rooij 				      codec->extstat,
675f9eb1409SOrion Hodson 				      ac97_rdcd(codec, AC97_REGEXT_STAT) &
676b60e55dbSGuido van Rooij 				      AC97_EXTCAPS);
6779ec437a3SCameron Grant 	}
6789ec437a3SCameron Grant 
679f9eb1409SOrion Hodson 	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
6809ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
68166ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
6829ec437a3SCameron Grant 	return 0;
6839ec437a3SCameron Grant }
6849ec437a3SCameron Grant 
685987e5972SCameron Grant struct ac97_info *
6860f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
687987e5972SCameron Grant {
688987e5972SCameron Grant 	struct ac97_info *codec;
689987e5972SCameron Grant 
6900f55ac6cSCameron Grant 	codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
6910f55ac6cSCameron Grant 	if (codec == NULL)
6920f55ac6cSCameron Grant 		return NULL;
6930f55ac6cSCameron Grant 
69466ef8af5SCameron Grant 	snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
695489c22ebSJohn Baldwin 	codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
696a163d034SWarner Losh 	codec->methods = kobj_create(cls, M_AC97, M_WAITOK);
6970f55ac6cSCameron Grant 	if (codec->methods == NULL) {
69866ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
69966ef8af5SCameron Grant 		snd_mtxfree(codec->lock);
7000f55ac6cSCameron Grant 		free(codec, M_AC97);
7010f55ac6cSCameron Grant 		return NULL;
702987e5972SCameron Grant 	}
7030f55ac6cSCameron Grant 
7040f55ac6cSCameron Grant 	codec->dev = dev;
7050f55ac6cSCameron Grant 	codec->devinfo = devinfo;
70679bb7d52SCameron Grant 	codec->flags = 0;
707987e5972SCameron Grant 	return codec;
708987e5972SCameron Grant }
709987e5972SCameron Grant 
71033dbf14aSCameron Grant void
71133dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec)
71233dbf14aSCameron Grant {
71366ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
7140f55ac6cSCameron Grant 	if (codec->methods != NULL)
7150f55ac6cSCameron Grant 		kobj_delete(codec->methods, M_AC97);
71666ef8af5SCameron Grant 	snd_mtxfree(codec->lock);
7170f55ac6cSCameron Grant 	free(codec, M_AC97);
71833dbf14aSCameron Grant }
71933dbf14aSCameron Grant 
72079bb7d52SCameron Grant void
72179bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val)
72279bb7d52SCameron Grant {
72379bb7d52SCameron Grant 	codec->flags = val;
72479bb7d52SCameron Grant }
72579bb7d52SCameron Grant 
72679bb7d52SCameron Grant u_int32_t
72779bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec)
72879bb7d52SCameron Grant {
72979bb7d52SCameron Grant 	return codec->flags;
73079bb7d52SCameron Grant }
73179bb7d52SCameron Grant 
7320f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
7330f55ac6cSCameron Grant 
734987e5972SCameron Grant static int
73566ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m)
736987e5972SCameron Grant {
737987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
738341f16ccSCameron Grant 	u_int32_t i, mask;
739341f16ccSCameron Grant 
74039004e69SCameron Grant 	if (codec == NULL)
74139004e69SCameron Grant 		return -1;
742341f16ccSCameron Grant 
743e620d959SCameron Grant 	if (ac97_initmixer(codec))
744e620d959SCameron Grant 		return -1;
745341f16ccSCameron Grant 
746341f16ccSCameron Grant 	mask = 0;
747341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
748341f16ccSCameron Grant 		mask |= codec->mix[i].enable? 1 << i : 0;
749341f16ccSCameron Grant 	mix_setdevs(m, mask);
750341f16ccSCameron Grant 
751341f16ccSCameron Grant 	mask = 0;
752341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
753341f16ccSCameron Grant 		mask |= codec->mix[i].recidx? 1 << i : 0;
754341f16ccSCameron Grant 	mix_setrecdevs(m, mask);
755987e5972SCameron Grant 	return 0;
756987e5972SCameron Grant }
757987e5972SCameron Grant 
758987e5972SCameron Grant static int
75966ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m)
76033dbf14aSCameron Grant {
76133dbf14aSCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
762341f16ccSCameron Grant 
76333dbf14aSCameron Grant 	if (codec == NULL)
76433dbf14aSCameron Grant 		return -1;
76533dbf14aSCameron Grant 	/*
76633dbf14aSCameron Grant 	if (ac97_uninitmixer(codec))
76733dbf14aSCameron Grant 		return -1;
76833dbf14aSCameron Grant 	*/
76933dbf14aSCameron Grant 	ac97_destroy(codec);
77033dbf14aSCameron Grant 	return 0;
77133dbf14aSCameron Grant }
77233dbf14aSCameron Grant 
77333dbf14aSCameron Grant static int
77466ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m)
7759ec437a3SCameron Grant {
7769ec437a3SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
7779ec437a3SCameron Grant 
7789ec437a3SCameron Grant 	if (codec == NULL)
7799ec437a3SCameron Grant 		return -1;
7809ec437a3SCameron Grant 	return ac97_reinitmixer(codec);
7819ec437a3SCameron Grant }
7829ec437a3SCameron Grant 
7839ec437a3SCameron Grant static int
78466ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
785987e5972SCameron Grant {
786987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
787341f16ccSCameron Grant 
78839004e69SCameron Grant 	if (codec == NULL)
78939004e69SCameron Grant 		return -1;
790987e5972SCameron Grant 	return ac97_setmixer(codec, dev, left, right);
791987e5972SCameron Grant }
792987e5972SCameron Grant 
793987e5972SCameron Grant static int
79466ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
795987e5972SCameron Grant {
796987e5972SCameron Grant 	int i;
797987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
798341f16ccSCameron Grant 
79939004e69SCameron Grant 	if (codec == NULL)
80039004e69SCameron Grant 		return -1;
801987e5972SCameron Grant 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
80239004e69SCameron Grant 		if ((src & (1 << i)) != 0)
80339004e69SCameron Grant 			break;
804987e5972SCameron Grant 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
805987e5972SCameron Grant }
806987e5972SCameron Grant 
8070f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = {
8080f55ac6cSCameron Grant     	KOBJMETHOD(mixer_init,		ac97mix_init),
8090f55ac6cSCameron Grant     	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
8100f55ac6cSCameron Grant     	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
8110f55ac6cSCameron Grant     	KOBJMETHOD(mixer_set,		ac97mix_set),
8120f55ac6cSCameron Grant     	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
8130f55ac6cSCameron Grant 	{ 0, 0 }
814987e5972SCameron Grant };
8150f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer);
8160f55ac6cSCameron Grant 
8170f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
8180f55ac6cSCameron Grant 
8190f55ac6cSCameron Grant kobj_class_t
8200f55ac6cSCameron Grant ac97_getmixerclass(void)
8210f55ac6cSCameron Grant {
8220f55ac6cSCameron Grant 	return &ac97mixer_class;
8230f55ac6cSCameron Grant }
8240f55ac6cSCameron Grant 
825987e5972SCameron Grant 
826