xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision b327ee514810059fd3fd70a66069880bf4eac64a)
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 },
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" },
1033c6b655dSMathew Kanner 	{ 0x44543000, "Diamond Technology" },
10461a0da1dSOrion Hodson 	{ 0x454d4300, "eMicro" },
10519921b23SOrion Hodson 	{ 0x45838300, "ESS Technology" },
1063c6b655dSMathew Kanner 	{ 0x48525300, "Intersil" },
10719921b23SOrion Hodson 	{ 0x49434500, "ICEnsemble" },
1083c6b655dSMathew Kanner 	{ 0x49544500, "ITE, Inc." },
10919921b23SOrion Hodson 	{ 0x4e534300, "National Semiconductor" },
11019921b23SOrion Hodson 	{ 0x50534300, "Philips Semiconductor" },
11119921b23SOrion Hodson 	{ 0x83847600, "SigmaTel" },
1123c6b655dSMathew Kanner 	{ 0x53494c00, "Silicon Laboratories" },
11319921b23SOrion Hodson 	{ 0x54524100, "TriTech" },
1143c6b655dSMathew Kanner 	{ 0x54584e00, "Texas Instruments" },
11519921b23SOrion Hodson 	{ 0x56494100, "VIA Technologies" },
1163c6b655dSMathew Kanner 	{ 0x57454300, "Winbond" },
11719921b23SOrion Hodson 	{ 0x574d4c00, "Wolfson" },
11819921b23SOrion Hodson 	{ 0x594d4800, "Yamaha" },
11986336196SAlexander Leidinger 	/*
12086336196SAlexander Leidinger 	 * XXX This is a fluke, really! The real vendor
12186336196SAlexander Leidinger 	 * should be SigmaTel, not this! This should be
12286336196SAlexander Leidinger 	 * removed someday!
12386336196SAlexander Leidinger 	 */
1246f0182bdSOrion Hodson 	{ 0x01408300, "Creative" },
12519921b23SOrion Hodson 	{ 0x00000000, NULL }
12619921b23SOrion Hodson };
12719921b23SOrion Hodson 
128987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = {
12919921b23SOrion Hodson 	{ 0x41445303, 0x00, 0, "AD1819",	0 },
13019921b23SOrion Hodson 	{ 0x41445340, 0x00, 0, "AD1881",	0 },
13119921b23SOrion Hodson 	{ 0x41445348, 0x00, 0, "AD1881A",	0 },
13219921b23SOrion Hodson 	{ 0x41445360, 0x00, 0, "AD1885",	0 },
13319921b23SOrion Hodson 	{ 0x41445361, 0x00, 0, "AD1886", 	ad1886_patch },
134ba548c64SOrion Hodson 	{ 0x41445362, 0x00, 0, "AD1887", 	0 },
135ba548c64SOrion Hodson 	{ 0x41445363, 0x00, 0, "AD1886A", 	0 },
136c5cb8d60SScott Long 	{ 0x41445368, 0x00, 0, "AD1888", 	ad198x_patch },
137a52604cfSOrion Hodson 	{ 0x41445370, 0x00, 0, "AD1980",	ad198x_patch },
138ba548c64SOrion Hodson 	{ 0x41445372, 0x00, 0, "AD1981A",	0 },
139ba548c64SOrion Hodson 	{ 0x41445374, 0x00, 0, "AD1981B",	0 },
140a52604cfSOrion Hodson 	{ 0x41445375, 0x00, 0, "AD1985",	ad198x_patch },
14119921b23SOrion Hodson 	{ 0x414b4d00, 0x00, 1, "AK4540", 	0 },
14219921b23SOrion Hodson 	{ 0x414b4d01, 0x00, 1, "AK4542", 	0 },
14319921b23SOrion Hodson 	{ 0x414b4d02, 0x00, 1, "AK4543", 	0 },
1443c6b655dSMathew Kanner 	{ 0x414b4d06, 0x00, 0, "AK4544A",	0 },
1453c6b655dSMathew Kanner 	{ 0x454b4d07, 0x00, 0, "AK4545",	0 },
14619921b23SOrion Hodson 	{ 0x414c4320, 0x0f, 0, "ALC100",	0 },
1479963235aSOrion Hodson 	{ 0x414c4730, 0x0f, 0, "ALC101",	0 },
14819921b23SOrion Hodson 	{ 0x414c4710, 0x0f, 0, "ALC200", 	0 },
14919921b23SOrion Hodson 	{ 0x414c4740, 0x0f, 0, "ALC202", 	0 },
15019921b23SOrion Hodson 	{ 0x414c4720, 0x0f, 0, "ALC650", 	0 },
151b327ee51SAriff Abdullah 	{ 0x414c4752, 0x0f, 0, "ALC250",	0 },
1525197cdc1SDag-Erling Smørgrav 	{ 0x414c4760, 0x0f, 0, "ALC655",	0 },
15394ed763dSJun Kuriyama 	{ 0x414c4780, 0x0f, 0, "ALC658",	0 },
1543c6b655dSMathew Kanner 	{ 0x414c4790, 0x0f, 0, "ALC850",	0 },
15519921b23SOrion Hodson 	{ 0x43525900, 0x07, 0, "CS4297", 	0 },
15619921b23SOrion Hodson 	{ 0x43525910, 0x07, 0, "CS4297A", 	0 },
15719921b23SOrion Hodson 	{ 0x43525920, 0x07, 0, "CS4294/98",	0 },
158e5728f83SMIHIRA Sanpei Yoshiro 	{ 0x4352592d, 0x07, 0, "CS4294",	0 },
15919921b23SOrion Hodson 	{ 0x43525930, 0x07, 0, "CS4299",	0 },
16019921b23SOrion Hodson 	{ 0x43525940, 0x07, 0, "CS4201",	0 },
161b2a0f525SOrion Hodson 	{ 0x43525958, 0x07, 0, "CS4205",	0 },
16219921b23SOrion Hodson 	{ 0x43525960, 0x07, 0, "CS4291A",	0 },
163cb44f623SAlexander Leidinger 	{ 0x434d4961, 0x00, 0, "CMI9739",	cmi9739_patch },
16419921b23SOrion Hodson 	{ 0x434d4941, 0x00, 0, "CMI9738",	0 },
165cb44f623SAlexander Leidinger 	{ 0x434d4978, 0x00, 0, "CMI9761",	0 },
166cb44f623SAlexander Leidinger 	{ 0x434d4982, 0x00, 0, "CMI9761",	0 },
167cb44f623SAlexander Leidinger 	{ 0x434d4983, 0x00, 0, "CMI9761",	0 },
1683c6b655dSMathew Kanner 	{ 0x43585421, 0x00, 0, "HSD11246",	0 },
1693c6b655dSMathew Kanner 	{ 0x43585428, 0x07, 0, "CX20468",	0 },
17080138937SAriff Abdullah 	{ 0x43585430, 0x00, 0, "CX20468-21",	0 },
1713c6b655dSMathew Kanner 	{ 0x44543000, 0x00, 0, "DT0398",	0 },
17261a0da1dSOrion Hodson 	{ 0x454d4323, 0x00, 0, "EM28023",	0 },
17361a0da1dSOrion Hodson 	{ 0x454d4328, 0x00, 0, "EM28028",	0 },
17419921b23SOrion Hodson 	{ 0x45838308, 0x00, 0, "ES1988",	0 }, /* Formerly ES1921(?) */
1753c6b655dSMathew Kanner 	{ 0x48525300, 0x00, 0, "HMP9701",	0 },
17619921b23SOrion Hodson 	{ 0x49434501, 0x00, 0, "ICE1230",	0 },
17719921b23SOrion Hodson 	{ 0x49434511, 0x00, 0, "ICE1232",	0 },
17819921b23SOrion Hodson 	{ 0x49434514, 0x00, 0, "ICE1232A",	0 },
17949fd6905SOrion Hodson 	{ 0x49434551, 0x03, 0, "VT1616",	0 }, /* Via badged ICE */
1803c6b655dSMathew Kanner 	{ 0x49544520, 0x00, 0, "ITE2226E",	0 },
1813c6b655dSMathew Kanner 	{ 0x49544560, 0x07, 0, "ITE2646E",	0 }, /* XXX: patch needed */
18219921b23SOrion Hodson 	{ 0x4e534340, 0x00, 0, "LM4540",	0 }, /* Spec blank on revid */
18319921b23SOrion Hodson 	{ 0x4e534343, 0x00, 0, "LM4543",	0 }, /* Ditto */
18419921b23SOrion Hodson 	{ 0x4e534346, 0x00, 0, "LM4546A",	0 },
18519921b23SOrion Hodson 	{ 0x4e534348, 0x00, 0, "LM4548A",	0 },
1863c6b655dSMathew Kanner 	{ 0x4e534331, 0x00, 0, "LM4549",	0 },
18719921b23SOrion Hodson 	{ 0x4e534349, 0x00, 0, "LM4549A",	0 },
18819921b23SOrion Hodson 	{ 0x4e534350, 0x00, 0, "LM4550",	0 },
18919921b23SOrion Hodson 	{ 0x50534301, 0x00, 0, "UCB1510",	0 },
19019921b23SOrion Hodson 	{ 0x50534304, 0x00, 0, "UCB1400",	0 },
19119921b23SOrion Hodson 	{ 0x83847600, 0x00, 0, "STAC9700/83/84",	0 },
19219921b23SOrion Hodson 	{ 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
19319921b23SOrion Hodson 	{ 0x83847605, 0x00, 0, "STAC9704",	0 },
19419921b23SOrion Hodson 	{ 0x83847608, 0x00, 0, "STAC9708/11",	0 },
19519921b23SOrion Hodson 	{ 0x83847609, 0x00, 0, "STAC9721/23",	0 },
19619921b23SOrion Hodson 	{ 0x83847644, 0x00, 0, "STAC9744/45",	0 },
19719921b23SOrion Hodson 	{ 0x83847650, 0x00, 0, "STAC9750/51",	0 },
19819921b23SOrion Hodson 	{ 0x83847652, 0x00, 0, "STAC9752/53",	0 },
19919921b23SOrion Hodson 	{ 0x83847656, 0x00, 0, "STAC9756/57",	0 },
20019921b23SOrion Hodson 	{ 0x83847658, 0x00, 0, "STAC9758/59",	0 },
20119921b23SOrion Hodson 	{ 0x83847660, 0x00, 0, "STAC9760/61",	0 }, /* Extrapolated */
20219921b23SOrion Hodson 	{ 0x83847662, 0x00, 0, "STAC9762/63",	0 }, /* Extrapolated */
203cb44f623SAlexander Leidinger 	{ 0x83847666, 0x00, 0, "STAC9766/67",	0 },
20419921b23SOrion Hodson 	{ 0x53494c22, 0x00, 0, "Si3036",	0 },
20519921b23SOrion Hodson 	{ 0x53494c23, 0x00, 0, "Si3038",	0 },
20619921b23SOrion Hodson 	{ 0x54524103, 0x00, 0, "TR28023",	0 }, /* Extrapolated */
20719921b23SOrion Hodson 	{ 0x54524106, 0x00, 0, "TR28026",	0 },
20819921b23SOrion Hodson 	{ 0x54524108, 0x00, 0, "TR28028",	0 },
20919921b23SOrion Hodson 	{ 0x54524123, 0x00, 0, "TR28602",	0 },
2103c6b655dSMathew Kanner 	{ 0x54524e03, 0x07, 0, "TLV320AIC27",	0 },
2113c6b655dSMathew Kanner 	{ 0x54584e20, 0x00, 0, "TLC320AD90",	0 },
21219921b23SOrion Hodson 	{ 0x56494161, 0x00, 0, "VIA1612A",      0 },
21380138937SAriff Abdullah 	{ 0x56494170, 0x00, 0, "VIA1617A",      0 },
21419921b23SOrion Hodson 	{ 0x574d4c00, 0x00, 0, "WM9701A",	0 },
21519921b23SOrion Hodson 	{ 0x574d4c03, 0x00, 0, "WM9703/4/7/8",	0 },
21619921b23SOrion Hodson 	{ 0x574d4c04, 0x00, 0, "WM9704Q",	0 },
21719921b23SOrion Hodson 	{ 0x574d4c05, 0x00, 0, "WM9705/10",	0 },
2183c6b655dSMathew Kanner 	{ 0x574d4d09, 0x00, 0, "WM9709",	0 },
2193c6b655dSMathew Kanner 	{ 0x574d4c12, 0x00, 0, "WM9711/12",	0 }, /* XXX: patch needed */
2203c6b655dSMathew Kanner 	{ 0x57454301, 0x00, 0, "W83971D",	0 },
22119921b23SOrion Hodson 	{ 0x594d4800, 0x00, 0, "YMF743",	0 },
22219921b23SOrion Hodson 	{ 0x594d4802, 0x00, 0, "YMF752",	0 },
22319921b23SOrion Hodson 	{ 0x594d4803, 0x00, 0, "YMF753",	0 },
22486336196SAlexander Leidinger 	/*
22586336196SAlexander Leidinger 	 * XXX This is a fluke, really! The real codec
22686336196SAlexander Leidinger 	 * should be STAC9704, not this! This should be
22786336196SAlexander Leidinger 	 * removed someday!
22886336196SAlexander Leidinger 	 */
2296f0182bdSOrion Hodson 	{ 0x01408384, 0x00, 0, "EV1938",	0 },
23019921b23SOrion Hodson 	{ 0, 0, 0, NULL, 0 }
231987e5972SCameron Grant };
232987e5972SCameron Grant 
233987e5972SCameron Grant static char *ac97enhancement[] = {
23404553e63SCameron Grant 	"no 3D Stereo Enhancement",
235987e5972SCameron Grant 	"Analog Devices Phat Stereo",
236987e5972SCameron Grant 	"Creative Stereo Enhancement",
237987e5972SCameron Grant 	"National Semi 3D Stereo Enhancement",
238987e5972SCameron Grant 	"Yamaha Ymersion",
239987e5972SCameron Grant 	"BBE 3D Stereo Enhancement",
240987e5972SCameron Grant 	"Crystal Semi 3D Stereo Enhancement",
241987e5972SCameron Grant 	"Qsound QXpander",
242987e5972SCameron Grant 	"Spatializer 3D Stereo Enhancement",
243987e5972SCameron Grant 	"SRS 3D Stereo Enhancement",
244987e5972SCameron Grant 	"Platform Tech 3D Stereo Enhancement",
245987e5972SCameron Grant 	"AKM 3D Audio",
246987e5972SCameron Grant 	"Aureal Stereo Enhancement",
247987e5972SCameron Grant 	"Aztech 3D Enhancement",
248987e5972SCameron Grant 	"Binaura 3D Audio Enhancement",
249987e5972SCameron Grant 	"ESS Technology Stereo Enhancement",
250987e5972SCameron Grant 	"Harman International VMAx",
251987e5972SCameron Grant 	"Nvidea 3D Stereo Enhancement",
252987e5972SCameron Grant 	"Philips Incredible Sound",
253987e5972SCameron Grant 	"Texas Instruments 3D Stereo Enhancement",
254987e5972SCameron Grant 	"VLSI Technology 3D Stereo Enhancement",
255987e5972SCameron Grant 	"TriTech 3D Stereo Enhancement",
256987e5972SCameron Grant 	"Realtek 3D Stereo Enhancement",
257987e5972SCameron Grant 	"Samsung 3D Stereo Enhancement",
258987e5972SCameron Grant 	"Wolfson Microelectronics 3D Enhancement",
259987e5972SCameron Grant 	"Delta Integration 3D Enhancement",
260987e5972SCameron Grant 	"SigmaTel 3D Enhancement",
261987e5972SCameron Grant 	"Reserved 27",
262987e5972SCameron Grant 	"Rockwell 3D Stereo Enhancement",
263987e5972SCameron Grant 	"Reserved 29",
264987e5972SCameron Grant 	"Reserved 30",
265987e5972SCameron Grant 	"Reserved 31"
266987e5972SCameron Grant };
267987e5972SCameron Grant 
268987e5972SCameron Grant static char *ac97feature[] = {
269987e5972SCameron Grant 	"mic channel",
270987e5972SCameron Grant 	"reserved",
271987e5972SCameron Grant 	"tone",
272987e5972SCameron Grant 	"simulated stereo",
273987e5972SCameron Grant 	"headphone",
274987e5972SCameron Grant 	"bass boost",
275987e5972SCameron Grant 	"18 bit DAC",
276987e5972SCameron Grant 	"20 bit DAC",
277987e5972SCameron Grant 	"18 bit ADC",
278987e5972SCameron Grant 	"20 bit ADC"
279987e5972SCameron Grant };
280987e5972SCameron Grant 
28139004e69SCameron Grant static char *ac97extfeature[] = {
28239004e69SCameron Grant 	"variable rate PCM",
28339004e69SCameron Grant 	"double rate PCM",
28439004e69SCameron Grant 	"reserved 1",
28539004e69SCameron Grant 	"variable rate mic",
28639004e69SCameron Grant 	"reserved 2",
28739004e69SCameron Grant 	"reserved 3",
28839004e69SCameron Grant 	"center DAC",
28939004e69SCameron Grant 	"surround DAC",
29039004e69SCameron Grant 	"LFE DAC",
29139004e69SCameron Grant 	"AMAP",
29239004e69SCameron Grant 	"reserved 4",
29339004e69SCameron Grant 	"reserved 5",
29439004e69SCameron Grant 	"reserved 6",
29539004e69SCameron Grant 	"reserved 7",
29639004e69SCameron Grant };
29739004e69SCameron Grant 
298f9eb1409SOrion Hodson u_int16_t
299f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg)
30039004e69SCameron Grant {
30186336196SAlexander Leidinger 	if (codec->flags & AC97_F_RDCD_BUG) {
30286336196SAlexander Leidinger 		u_int16_t i[2], j = 100;
30386336196SAlexander Leidinger 
30486336196SAlexander Leidinger 		i[0] = AC97_READ(codec->methods, codec->devinfo, reg);
30586336196SAlexander Leidinger 		i[1] = AC97_READ(codec->methods, codec->devinfo, reg);
30686336196SAlexander Leidinger 		while (i[0] != i[1] && j)
30786336196SAlexander Leidinger 			i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg);
30886336196SAlexander Leidinger #if 0
30986336196SAlexander Leidinger 		if (j < 100) {
31086336196SAlexander Leidinger 			device_printf(codec->dev, "%s(): Inconsistent register value at"
31186336196SAlexander Leidinger 					" 0x%08x (retry: %d)\n", __func__, reg, 100 - j);
31286336196SAlexander Leidinger 		}
31386336196SAlexander Leidinger #endif
31486336196SAlexander Leidinger 		return i[!(j & 1)];
31586336196SAlexander Leidinger 	}
3160f55ac6cSCameron Grant 	return AC97_READ(codec->methods, codec->devinfo, reg);
31739004e69SCameron Grant }
31839004e69SCameron Grant 
319f9eb1409SOrion Hodson void
320f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
32139004e69SCameron Grant {
3220f55ac6cSCameron Grant 	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
32339004e69SCameron Grant }
32439004e69SCameron Grant 
325c6d4b83aSOrion Hodson static void
326c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec)
327c6d4b83aSOrion Hodson {
328c6d4b83aSOrion Hodson 	u_int32_t i, ps;
329f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_RESET, 0);
330c6d4b83aSOrion Hodson 	for (i = 0; i < 500; i++) {
331f9eb1409SOrion Hodson 		ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
332c6d4b83aSOrion Hodson 		if (ps == AC97_POWER_STATUS)
333c6d4b83aSOrion Hodson 			return;
334c6d4b83aSOrion Hodson 		DELAY(1000);
335c6d4b83aSOrion Hodson 	}
336a825c6e5SOrion Hodson 	device_printf(codec->dev, "AC97 reset timed out.\n");
337c6d4b83aSOrion Hodson }
338c6d4b83aSOrion Hodson 
33939004e69SCameron Grant int
34039004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate)
34139004e69SCameron Grant {
34239004e69SCameron Grant 	u_int16_t v;
34339004e69SCameron Grant 
34439004e69SCameron Grant 	switch(which) {
34539004e69SCameron Grant 	case AC97_REGEXT_FDACRATE:
34639004e69SCameron Grant 	case AC97_REGEXT_SDACRATE:
34739004e69SCameron Grant 	case AC97_REGEXT_LDACRATE:
34839004e69SCameron Grant 	case AC97_REGEXT_LADCRATE:
34939004e69SCameron Grant 	case AC97_REGEXT_MADCRATE:
35039004e69SCameron Grant 		break;
35139004e69SCameron Grant 
35239004e69SCameron Grant 	default:
35339004e69SCameron Grant 		return -1;
35439004e69SCameron Grant 	}
35539004e69SCameron Grant 
35666ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
35739004e69SCameron Grant 	if (rate != 0) {
35839004e69SCameron Grant 		v = rate;
35939004e69SCameron Grant 		if (codec->extstat & AC97_EXTCAP_DRA)
36039004e69SCameron Grant 			v >>= 1;
361f9eb1409SOrion Hodson 		ac97_wrcd(codec, which, v);
36239004e69SCameron Grant 	}
363f9eb1409SOrion Hodson 	v = ac97_rdcd(codec, which);
36439004e69SCameron Grant 	if (codec->extstat & AC97_EXTCAP_DRA)
36539004e69SCameron Grant 		v <<= 1;
36666ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
36739004e69SCameron Grant 	return v;
36839004e69SCameron Grant }
36939004e69SCameron Grant 
37039004e69SCameron Grant int
37139004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
37239004e69SCameron Grant {
37339004e69SCameron Grant 	mode &= AC97_EXTCAPS;
374647fbfebSOrion Hodson 	if ((mode & ~codec->extcaps) != 0) {
375647fbfebSOrion Hodson 		device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
376647fbfebSOrion Hodson 			      mode);
37739004e69SCameron Grant 		return -1;
378647fbfebSOrion Hodson 	}
37966ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
380f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
381f9eb1409SOrion Hodson 	codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
38266ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
38339004e69SCameron Grant 	return (mode == codec->extstat)? 0 : -1;
38439004e69SCameron Grant }
38539004e69SCameron Grant 
3869ec437a3SCameron Grant u_int16_t
3879ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec)
3889ec437a3SCameron Grant {
3899ec437a3SCameron Grant 	return codec->extstat;
3909ec437a3SCameron Grant }
3919ec437a3SCameron Grant 
3929ec437a3SCameron Grant u_int16_t
3939ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec)
3949ec437a3SCameron Grant {
3959ec437a3SCameron Grant 	return codec->extcaps;
3969ec437a3SCameron Grant }
3979ec437a3SCameron Grant 
3985d91ad67SCameron Grant u_int16_t
3995d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec)
4005d91ad67SCameron Grant {
4015d91ad67SCameron Grant 	return codec->caps;
4025d91ad67SCameron Grant }
4035d91ad67SCameron Grant 
404987e5972SCameron Grant static int
405987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel)
406987e5972SCameron Grant {
407987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
408341f16ccSCameron Grant 
409987e5972SCameron Grant 	if (e->recidx > 0) {
410987e5972SCameron Grant 		int val = e->recidx - 1;
411987e5972SCameron Grant 		val |= val << 8;
41266ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
413f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REG_RECSEL, val);
41466ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
415987e5972SCameron Grant 		return 0;
41639004e69SCameron Grant 	} else
41739004e69SCameron Grant 		return -1;
418987e5972SCameron Grant }
419987e5972SCameron Grant 
420987e5972SCameron Grant static int
421987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
422987e5972SCameron Grant {
423987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
424341f16ccSCameron Grant 
425341f16ccSCameron Grant 	if (e->reg && e->enable && e->bits) {
42696524a52SOrion Hodson 		int mask, max, val, reg;
42796524a52SOrion Hodson 
42896524a52SOrion Hodson 		reg = (e->reg >= 0) ? e->reg : -e->reg;	/* AC97 register    */
42996524a52SOrion Hodson 		max = (1 << e->bits) - 1;		/* actual range	    */
43096524a52SOrion Hodson 		mask = (max << 8) | max;		/* bits of interest */
431987e5972SCameron Grant 
43239004e69SCameron Grant 		if (!e->stereo)
43339004e69SCameron Grant 			right = left;
43496524a52SOrion Hodson 
43596524a52SOrion Hodson 		/*
43696524a52SOrion Hodson 		 * Invert the range if the polarity requires so,
43796524a52SOrion Hodson 		 * then scale to 0..max-1 to compute the value to
43896524a52SOrion Hodson 		 * write into the codec, and scale back to 0..100
43996524a52SOrion Hodson 		 * for the return value.
44096524a52SOrion Hodson 		 */
441987e5972SCameron Grant 		if (e->reg > 0) {
442987e5972SCameron Grant 			left = 100 - left;
443987e5972SCameron Grant 			right = 100 - right;
444987e5972SCameron Grant 		}
445987e5972SCameron Grant 
446987e5972SCameron Grant 		left = (left * max) / 100;
447987e5972SCameron Grant 		right = (right * max) / 100;
448987e5972SCameron Grant 
449987e5972SCameron Grant 		val = (left << 8) | right;
450987e5972SCameron Grant 
451987e5972SCameron Grant 		left = (left * 100) / max;
452987e5972SCameron Grant 		right = (right * 100) / max;
453987e5972SCameron Grant 
454987e5972SCameron Grant 		if (e->reg > 0) {
455987e5972SCameron Grant 			left = 100 - left;
456987e5972SCameron Grant 			right = 100 - right;
457987e5972SCameron Grant 		}
458987e5972SCameron Grant 
45996524a52SOrion Hodson 		/*
46096524a52SOrion Hodson 		 * For mono controls, trim val and mask, also taking
46196524a52SOrion Hodson 		 * care of e->ofs (offset of control field).
46296524a52SOrion Hodson 		 */
46396524a52SOrion Hodson 		if (e->ofs) {
464987e5972SCameron Grant 			val &= max;
465987e5972SCameron Grant 			val <<= e->ofs;
46696524a52SOrion Hodson 			mask = (max << e->ofs);
46796524a52SOrion Hodson 		}
46896524a52SOrion Hodson 
46996524a52SOrion Hodson 		/*
47096524a52SOrion Hodson 		 * If we have a mute bit, add it to the mask and
47196524a52SOrion Hodson 		 * update val and set mute if both channels require a
47296524a52SOrion Hodson 		 * zero volume.
47396524a52SOrion Hodson 		 */
47496524a52SOrion Hodson 		if (e->mute == 1) {
47596524a52SOrion Hodson 			mask |= AC97_MUTE;
47696524a52SOrion Hodson 			if (left == 0 && right == 0)
47796524a52SOrion Hodson 				val = AC97_MUTE;
47896524a52SOrion Hodson 		}
47996524a52SOrion Hodson 
48096524a52SOrion Hodson 		/*
48196524a52SOrion Hodson 		 * If the mask bit is set, do not alter the other bits.
48296524a52SOrion Hodson 		 */
48396524a52SOrion Hodson 		snd_mtxlock(codec->lock);
484987e5972SCameron Grant 		if (e->mask) {
48586336196SAlexander Leidinger 			int cur = ac97_rdcd(codec, reg);
48696524a52SOrion Hodson 			val |= cur & ~(mask);
487987e5972SCameron Grant 		}
488f9eb1409SOrion Hodson 		ac97_wrcd(codec, reg, val);
48966ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
490987e5972SCameron Grant 		return left | (right << 8);
491341f16ccSCameron Grant 	} else {
49286336196SAlexander Leidinger #if 0
49386336196SAlexander Leidinger 		printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable);
49486336196SAlexander Leidinger #endif
49539004e69SCameron Grant 		return -1;
496987e5972SCameron Grant 	}
497341f16ccSCameron Grant }
498987e5972SCameron Grant 
499108082c4SOrion Hodson static void
500108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec)
501108082c4SOrion Hodson {
502cfd5696dSOrion Hodson 	int keep_ogain;
503cfd5696dSOrion Hodson 
504a52604cfSOrion Hodson 	/*
505cfd5696dSOrion Hodson 	 * By default, The ac97 aux_out register (0x04) corresponds to OSS's
506cfd5696dSOrion Hodson 	 * OGAIN setting.
507a52604cfSOrion Hodson 	 *
508cfd5696dSOrion Hodson 	 * We first check whether aux_out is a valid register.  If not
509cfd5696dSOrion Hodson 	 * we may not want to keep ogain.
510a52604cfSOrion Hodson 	 */
511cfd5696dSOrion Hodson 	keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000;
512a52604cfSOrion Hodson 
513a52604cfSOrion Hodson 	/*
514a52604cfSOrion Hodson 	 * Determine what AUX_OUT really means, it can be:
515108082c4SOrion Hodson 	 *
516108082c4SOrion Hodson 	 * 1. Headphone out.
517108082c4SOrion Hodson 	 * 2. 4-Channel Out
518108082c4SOrion Hodson 	 * 3. True line level out (effectively master volume).
519108082c4SOrion Hodson 	 *
520108082c4SOrion Hodson 	 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
521108082c4SOrion Hodson 	 */
522a52604cfSOrion Hodson 	if (codec->extcaps & AC97_EXTCAP_SDAC &&
523f9eb1409SOrion Hodson 	    ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
524cfd5696dSOrion Hodson 		codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND;
525cfd5696dSOrion Hodson 		keep_ogain = 1;
526cfd5696dSOrion Hodson 	}
527cfd5696dSOrion Hodson 
528cfd5696dSOrion Hodson 	if (keep_ogain == 0) {
529cfd5696dSOrion Hodson 		bzero(&codec->mix[SOUND_MIXER_OGAIN],
530cfd5696dSOrion Hodson 		      sizeof(codec->mix[SOUND_MIXER_OGAIN]));
531108082c4SOrion Hodson 	}
532a52604cfSOrion Hodson }
533a52604cfSOrion Hodson 
534a52604cfSOrion Hodson static void
535a52604cfSOrion Hodson ac97_fix_tone(struct ac97_info *codec)
536a52604cfSOrion Hodson {
537a52604cfSOrion Hodson 	/* Hide treble and bass if they don't exist */
538a52604cfSOrion Hodson 	if ((codec->caps & AC97_CAP_TONE) == 0) {
539a52604cfSOrion Hodson 		bzero(&codec->mix[SOUND_MIXER_BASS],
540a52604cfSOrion Hodson 		      sizeof(codec->mix[SOUND_MIXER_BASS]));
541a52604cfSOrion Hodson 		bzero(&codec->mix[SOUND_MIXER_TREBLE],
542a52604cfSOrion Hodson 		      sizeof(codec->mix[SOUND_MIXER_TREBLE]));
543a52604cfSOrion Hodson 	}
544108082c4SOrion Hodson }
545108082c4SOrion Hodson 
546cb44f623SAlexander Leidinger static void
547cb44f623SAlexander Leidinger ac97_fix_volume(struct ac97_info *codec)
548cb44f623SAlexander Leidinger {
549cb44f623SAlexander Leidinger     	struct snddev_info *d = device_get_softc(codec->dev);
550cb44f623SAlexander Leidinger 
551cb44f623SAlexander Leidinger #if 0
552cb44f623SAlexander Leidinger 	/* XXX For the sake of debugging purposes */
553cb44f623SAlexander Leidinger 	ac97_wrcd(codec, AC97_MIX_PCM, 0);
554cb44f623SAlexander Leidinger 	bzero(&codec->mix[SOUND_MIXER_PCM],
555cb44f623SAlexander Leidinger 		sizeof(codec->mix[SOUND_MIXER_PCM]));
556cb44f623SAlexander Leidinger 	codec->flags |= AC97_F_SOFTVOL;
557cb44f623SAlexander Leidinger 	if (d)
558cb44f623SAlexander Leidinger 		d->flags |= SD_F_SOFTVOL;
559cb44f623SAlexander Leidinger 	return;
560cb44f623SAlexander Leidinger #endif
561cb44f623SAlexander Leidinger 	switch (codec->id) {
562cb44f623SAlexander Leidinger 		case 0x434d4941:	/* CMI9738 */
563cb44f623SAlexander Leidinger 		case 0x434d4961:	/* CMI9739 */
564cb44f623SAlexander Leidinger 		case 0x434d4978:	/* CMI9761 */
565cb44f623SAlexander Leidinger 		case 0x434d4982:	/* CMI9761 */
566cb44f623SAlexander Leidinger 		case 0x434d4983:	/* CMI9761 */
567cb44f623SAlexander Leidinger 			ac97_wrcd(codec, AC97_MIX_PCM, 0);
568cb44f623SAlexander Leidinger 			break;
569cb44f623SAlexander Leidinger 		default:
570cb44f623SAlexander Leidinger 			return;
571cb44f623SAlexander Leidinger 			break;
572cb44f623SAlexander Leidinger 	}
573cb44f623SAlexander Leidinger 	bzero(&codec->mix[SOUND_MIXER_PCM],
574cb44f623SAlexander Leidinger 			sizeof(codec->mix[SOUND_MIXER_PCM]));
575cb44f623SAlexander Leidinger 	codec->flags |= AC97_F_SOFTVOL;
576cb44f623SAlexander Leidinger 	if (d)
577cb44f623SAlexander Leidinger 		d->flags |= SD_F_SOFTVOL;
578cb44f623SAlexander Leidinger }
579cb44f623SAlexander Leidinger 
58019921b23SOrion Hodson static const char*
58119921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
58219921b23SOrion Hodson {
58319921b23SOrion Hodson 	if (cname == NULL) {
58419921b23SOrion Hodson 		sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
58519921b23SOrion Hodson 		return buf;
58619921b23SOrion Hodson 	}
58719921b23SOrion Hodson 
58819921b23SOrion Hodson 	if (vname == NULL) vname = "Unknown";
58919921b23SOrion Hodson 
59019921b23SOrion Hodson 	if (bootverbose) {
59119921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
59219921b23SOrion Hodson 	} else {
59319921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec", vname, cname);
59419921b23SOrion Hodson 	}
59519921b23SOrion Hodson 	return buf;
59619921b23SOrion Hodson }
59719921b23SOrion Hodson 
598987e5972SCameron Grant static unsigned
59939004e69SCameron Grant ac97_initmixer(struct ac97_info *codec)
600987e5972SCameron Grant {
601f9eb1409SOrion Hodson 	ac97_patch codec_patch;
60219921b23SOrion Hodson 	const char *cname, *vname;
60319921b23SOrion Hodson 	char desc[80];
60419921b23SOrion Hodson 	u_int8_t model, step;
60586336196SAlexander Leidinger 	unsigned i, j, k, bit, old;
606987e5972SCameron Grant 	u_int32_t id;
60786336196SAlexander Leidinger 	int reg;
608987e5972SCameron Grant 
60966ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
6100f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
611cd2c103aSCameron Grant 	if (codec->count == 0) {
61204553e63SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
61366ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
61404553e63SCameron Grant 		return ENODEV;
61504553e63SCameron Grant 	}
6169ec437a3SCameron Grant 
617f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
618c6d4b83aSOrion Hodson 	ac97_reset(codec);
619f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
620987e5972SCameron Grant 
621f9eb1409SOrion Hodson 	i = ac97_rdcd(codec, AC97_REG_RESET);
62286336196SAlexander Leidinger 	j = ac97_rdcd(codec, AC97_REG_RESET);
62386336196SAlexander Leidinger 	/*
62486336196SAlexander Leidinger 	 * Let see if this codec can return consistent value.
62586336196SAlexander Leidinger 	 * If not, turn on aggressive read workaround
62686336196SAlexander Leidinger 	 * (STAC9704 comes in mind).
62786336196SAlexander Leidinger 	 */
62886336196SAlexander Leidinger 	if (i != j) {
62986336196SAlexander Leidinger 		codec->flags |= AC97_F_RDCD_BUG;
63086336196SAlexander Leidinger 		i = ac97_rdcd(codec, AC97_REG_RESET);
63186336196SAlexander Leidinger 	}
632987e5972SCameron Grant 	codec->caps = i & 0x03ff;
633987e5972SCameron Grant 	codec->se =  (i & 0x7c00) >> 10;
634987e5972SCameron Grant 
635f9eb1409SOrion Hodson 	id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
636e620d959SCameron Grant 	if (id == 0 || id == 0xffffffff) {
637e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
63866ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
639e620d959SCameron Grant 		return ENODEV;
640e620d959SCameron Grant 	}
6416b4b88f7SCameron Grant 
64219921b23SOrion Hodson 	codec->id = id;
643cd2c103aSCameron Grant 	codec->noext = 0;
644f9eb1409SOrion Hodson 	codec_patch = NULL;
64519921b23SOrion Hodson 
64619921b23SOrion Hodson 	cname = NULL;
64719921b23SOrion Hodson 	model = step = 0;
648cd2c103aSCameron Grant 	for (i = 0; ac97codecid[i].id; i++) {
64919921b23SOrion Hodson 		u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
65019921b23SOrion Hodson 		if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
651cd2c103aSCameron Grant 			codec->noext = ac97codecid[i].noext;
652f9eb1409SOrion Hodson 			codec_patch = ac97codecid[i].patch;
65319921b23SOrion Hodson 			cname = ac97codecid[i].name;
65419921b23SOrion Hodson 			model = (id & modelmask) & 0xff;
65519921b23SOrion Hodson 			step = (id & ~modelmask) & 0xff;
65619921b23SOrion Hodson 			break;
65719921b23SOrion Hodson 		}
65819921b23SOrion Hodson 	}
65919921b23SOrion Hodson 
66019921b23SOrion Hodson 	vname = NULL;
66119921b23SOrion Hodson 	for (i = 0; ac97vendorid[i].id; i++) {
66219921b23SOrion Hodson 		if (ac97vendorid[i].id == (id & 0xffffff00)) {
66319921b23SOrion Hodson 			vname = ac97vendorid[i].name;
66419921b23SOrion Hodson 			break;
665cd2c103aSCameron Grant 		}
666cd2c103aSCameron Grant 	}
6676b4b88f7SCameron Grant 
668cd2c103aSCameron Grant 	codec->extcaps = 0;
669cd2c103aSCameron Grant 	codec->extid = 0;
670cd2c103aSCameron Grant 	codec->extstat = 0;
6716a6ee5bbSCameron Grant 	if (!codec->noext) {
672f9eb1409SOrion Hodson 		i = ac97_rdcd(codec, AC97_REGEXT_ID);
6736a6ee5bbSCameron Grant 		if (i != 0xffff) {
67439004e69SCameron Grant 			codec->extcaps = i & 0x3fff;
67539004e69SCameron Grant 			codec->extid =  (i & 0xc000) >> 14;
676f9eb1409SOrion Hodson 			codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
6776b4b88f7SCameron Grant 		}
6786a6ee5bbSCameron Grant 	}
679987e5972SCameron Grant 
680341f16ccSCameron Grant 	for (i = 0; i < 32; i++) {
681108082c4SOrion Hodson 		codec->mix[i] = ac97mixtable_default[i];
682108082c4SOrion Hodson 	}
683108082c4SOrion Hodson 	ac97_fix_auxout(codec);
684a52604cfSOrion Hodson 	ac97_fix_tone(codec);
685cb44f623SAlexander Leidinger 	ac97_fix_volume(codec);
686f9eb1409SOrion Hodson 	if (codec_patch)
687f9eb1409SOrion Hodson 		codec_patch(codec);
688108082c4SOrion Hodson 
689108082c4SOrion Hodson 	for (i = 0; i < 32; i++) {
69033c878f0SCameron Grant 		k = codec->noext? codec->mix[i].enable : 1;
69186336196SAlexander Leidinger 		reg = codec->mix[i].reg;
69286336196SAlexander Leidinger 		if (reg < 0)
69386336196SAlexander Leidinger 			reg = -reg;
69486336196SAlexander Leidinger 		if (k && reg) {
69586336196SAlexander Leidinger 			j = old = ac97_rdcd(codec, reg);
69686336196SAlexander Leidinger 			/*
69786336196SAlexander Leidinger 			 * Test for mute bit (except for AC97_MIX_TONE,
69886336196SAlexander Leidinger 			 * where we simply assume it as available).
69986336196SAlexander Leidinger 			 */
70086336196SAlexander Leidinger 			if (codec->mix[i].mute) {
70186336196SAlexander Leidinger 				ac97_wrcd(codec, reg, j | 0x8000);
70286336196SAlexander Leidinger 				j = ac97_rdcd(codec, reg);
70386336196SAlexander Leidinger 			} else
704fe435202SAlexander Leidinger 				j |= 0x8000;
70586336196SAlexander Leidinger 			if ((j & 0x8000)) {
70686336196SAlexander Leidinger 				/*
70786336196SAlexander Leidinger 				 * Test whether the control width should be
70886336196SAlexander Leidinger 				 * 4, 5 or 6 bit. For 5bit register, we should
70986336196SAlexander Leidinger 				 * test it whether it's really 5 or 6bit. Leave
71086336196SAlexander Leidinger 				 * 4bit register alone, because sometimes an
71186336196SAlexander Leidinger 				 * attempt to write past 4th bit may cause
71286336196SAlexander Leidinger 				 * incorrect result especially for AC97_MIX_BEEP
71386336196SAlexander Leidinger 				 * (ac97 2.3).
71486336196SAlexander Leidinger 				 */
71586336196SAlexander Leidinger 				bit = codec->mix[i].bits;
71686336196SAlexander Leidinger 				if (bit == 5)
71786336196SAlexander Leidinger 					bit++;
71886336196SAlexander Leidinger 				j = ((1 << bit) - 1) << codec->mix[i].ofs;
71986336196SAlexander Leidinger 				ac97_wrcd(codec, reg,
72086336196SAlexander Leidinger 					j | (codec->mix[i].mute ? 0x8000 : 0));
72186336196SAlexander Leidinger 				k = ac97_rdcd(codec, reg) & j;
72286336196SAlexander Leidinger 				k >>= codec->mix[i].ofs;
72386336196SAlexander Leidinger 				if (reg == AC97_MIX_TONE &&
72486336196SAlexander Leidinger 							((k & 0x0001) == 0x0000))
725fe435202SAlexander Leidinger 					k >>= 1;
72686336196SAlexander Leidinger 				for (j = 0; k >> j; j++)
72786336196SAlexander Leidinger 					;
728fe435202SAlexander Leidinger 				if (j != 0) {
729fe435202SAlexander Leidinger #if 0
73086336196SAlexander Leidinger 					device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n",
73186336196SAlexander Leidinger 						i, k, bit, codec->mix[i].bits, j);
732fe435202SAlexander Leidinger #endif
73386336196SAlexander Leidinger 					codec->mix[i].enable = 1;
73486336196SAlexander Leidinger 					codec->mix[i].bits = j;
735fe435202SAlexander Leidinger 				} else
736fe435202SAlexander Leidinger 					codec->mix[i].enable = 0;
737fe435202SAlexander Leidinger 			} else
738fe435202SAlexander Leidinger 				codec->mix[i].enable = 0;
73986336196SAlexander Leidinger 			ac97_wrcd(codec, reg, old);
740fe435202SAlexander Leidinger 		}
741fe435202SAlexander Leidinger #if 0
742fe435202SAlexander Leidinger 		printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits);
743fe435202SAlexander Leidinger #endif
744341f16ccSCameron Grant 	}
745987e5972SCameron Grant 
74619921b23SOrion Hodson 	device_printf(codec->dev, "<%s>\n",
74719921b23SOrion Hodson 		      ac97_hw_desc(codec->id, vname, cname, desc));
748a825c6e5SOrion Hodson 
749987e5972SCameron Grant 	if (bootverbose) {
75086336196SAlexander Leidinger 		if (codec->flags & AC97_F_RDCD_BUG)
75186336196SAlexander Leidinger 			device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n");
752cb44f623SAlexander Leidinger 		if (codec->flags & AC97_F_SOFTVOL)
753cb44f623SAlexander Leidinger 			device_printf(codec->dev, "Soft PCM volume\n");
75419921b23SOrion Hodson 		device_printf(codec->dev, "Codec features ");
75539004e69SCameron Grant 		for (i = j = 0; i < 10; i++)
75639004e69SCameron Grant 			if (codec->caps & (1 << i))
75739004e69SCameron Grant 				printf("%s%s", j++? ", " : "", ac97feature[i]);
75839004e69SCameron Grant 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
759987e5972SCameron Grant 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
76039004e69SCameron Grant 
76139004e69SCameron Grant 		if (codec->extcaps != 0 || codec->extid) {
76219921b23SOrion Hodson 			device_printf(codec->dev, "%s codec",
76319921b23SOrion Hodson 				      codec->extid? "Secondary" : "Primary");
76439004e69SCameron Grant 			if (codec->extcaps)
76539004e69SCameron Grant 				printf(" extended features ");
76639004e69SCameron Grant 			for (i = j = 0; i < 14; i++)
76739004e69SCameron Grant 				if (codec->extcaps & (1 << i))
76839004e69SCameron Grant 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
76939004e69SCameron Grant 			printf("\n");
77039004e69SCameron Grant 		}
771987e5972SCameron Grant 	}
772987e5972SCameron Grant 
773fe435202SAlexander Leidinger 	i = 0;
774fe435202SAlexander Leidinger 	while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) {
775fe435202SAlexander Leidinger 		if (++i == 100) {
77603a00905SCameron Grant 			device_printf(codec->dev, "ac97 codec reports dac not ready\n");
777fe435202SAlexander Leidinger 			break;
778fe435202SAlexander Leidinger 		}
779fe435202SAlexander Leidinger 		DELAY(1000);
780fe435202SAlexander Leidinger 	}
781fe435202SAlexander Leidinger 	if (bootverbose)
782fe435202SAlexander Leidinger 		device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i);
78366ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
784987e5972SCameron Grant 	return 0;
785987e5972SCameron Grant }
786987e5972SCameron Grant 
7879ec437a3SCameron Grant static unsigned
7889ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec)
7899ec437a3SCameron Grant {
79066ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
7910f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
7929ec437a3SCameron Grant 	if (codec->count == 0) {
7939ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
79466ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
7959ec437a3SCameron Grant 		return ENODEV;
7969ec437a3SCameron Grant 	}
7979ec437a3SCameron Grant 
798f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
799c6d4b83aSOrion Hodson 	ac97_reset(codec);
800f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
8019ec437a3SCameron Grant 
8029ec437a3SCameron Grant 	if (!codec->noext) {
803f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
804f9eb1409SOrion Hodson 		if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
805b60e55dbSGuido van Rooij 		    != codec->extstat)
8069ec437a3SCameron Grant 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
807b60e55dbSGuido van Rooij 				      codec->extstat,
808f9eb1409SOrion Hodson 				      ac97_rdcd(codec, AC97_REGEXT_STAT) &
809b60e55dbSGuido van Rooij 				      AC97_EXTCAPS);
8109ec437a3SCameron Grant 	}
8119ec437a3SCameron Grant 
812f9eb1409SOrion Hodson 	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
8139ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
81466ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
8159ec437a3SCameron Grant 	return 0;
8169ec437a3SCameron Grant }
8179ec437a3SCameron Grant 
818987e5972SCameron Grant struct ac97_info *
8190f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
820987e5972SCameron Grant {
821987e5972SCameron Grant 	struct ac97_info *codec;
822987e5972SCameron Grant 
8230f55ac6cSCameron Grant 	codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
8240f55ac6cSCameron Grant 	if (codec == NULL)
8250f55ac6cSCameron Grant 		return NULL;
8260f55ac6cSCameron Grant 
82766ef8af5SCameron Grant 	snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
828489c22ebSJohn Baldwin 	codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
829a163d034SWarner Losh 	codec->methods = kobj_create(cls, M_AC97, M_WAITOK);
8300f55ac6cSCameron Grant 	if (codec->methods == NULL) {
83166ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
83266ef8af5SCameron Grant 		snd_mtxfree(codec->lock);
8330f55ac6cSCameron Grant 		free(codec, M_AC97);
8340f55ac6cSCameron Grant 		return NULL;
835987e5972SCameron Grant 	}
8360f55ac6cSCameron Grant 
8370f55ac6cSCameron Grant 	codec->dev = dev;
8380f55ac6cSCameron Grant 	codec->devinfo = devinfo;
83979bb7d52SCameron Grant 	codec->flags = 0;
840987e5972SCameron Grant 	return codec;
841987e5972SCameron Grant }
842987e5972SCameron Grant 
84333dbf14aSCameron Grant void
84433dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec)
84533dbf14aSCameron Grant {
84666ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
8470f55ac6cSCameron Grant 	if (codec->methods != NULL)
8480f55ac6cSCameron Grant 		kobj_delete(codec->methods, M_AC97);
84966ef8af5SCameron Grant 	snd_mtxfree(codec->lock);
8500f55ac6cSCameron Grant 	free(codec, M_AC97);
85133dbf14aSCameron Grant }
85233dbf14aSCameron Grant 
85379bb7d52SCameron Grant void
85479bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val)
85579bb7d52SCameron Grant {
85679bb7d52SCameron Grant 	codec->flags = val;
85779bb7d52SCameron Grant }
85879bb7d52SCameron Grant 
85979bb7d52SCameron Grant u_int32_t
86079bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec)
86179bb7d52SCameron Grant {
86279bb7d52SCameron Grant 	return codec->flags;
86379bb7d52SCameron Grant }
86479bb7d52SCameron Grant 
8650f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
8660f55ac6cSCameron Grant 
867987e5972SCameron Grant static int
86866ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m)
869987e5972SCameron Grant {
870987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
871341f16ccSCameron Grant 	u_int32_t i, mask;
872341f16ccSCameron Grant 
87339004e69SCameron Grant 	if (codec == NULL)
87439004e69SCameron Grant 		return -1;
875341f16ccSCameron Grant 
876e620d959SCameron Grant 	if (ac97_initmixer(codec))
877e620d959SCameron Grant 		return -1;
878341f16ccSCameron Grant 
879341f16ccSCameron Grant 	mask = 0;
880341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
881341f16ccSCameron Grant 		mask |= codec->mix[i].enable? 1 << i : 0;
882341f16ccSCameron Grant 	mix_setdevs(m, mask);
883341f16ccSCameron Grant 
884341f16ccSCameron Grant 	mask = 0;
885341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
886341f16ccSCameron Grant 		mask |= codec->mix[i].recidx? 1 << i : 0;
887341f16ccSCameron Grant 	mix_setrecdevs(m, mask);
888987e5972SCameron Grant 	return 0;
889987e5972SCameron Grant }
890987e5972SCameron Grant 
891987e5972SCameron Grant static int
89266ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m)
89333dbf14aSCameron Grant {
89433dbf14aSCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
895341f16ccSCameron Grant 
89633dbf14aSCameron Grant 	if (codec == NULL)
89733dbf14aSCameron Grant 		return -1;
89833dbf14aSCameron Grant 	/*
89933dbf14aSCameron Grant 	if (ac97_uninitmixer(codec))
90033dbf14aSCameron Grant 		return -1;
90133dbf14aSCameron Grant 	*/
90233dbf14aSCameron Grant 	ac97_destroy(codec);
90333dbf14aSCameron Grant 	return 0;
90433dbf14aSCameron Grant }
90533dbf14aSCameron Grant 
90633dbf14aSCameron Grant static int
90766ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m)
9089ec437a3SCameron Grant {
9099ec437a3SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
9109ec437a3SCameron Grant 
9119ec437a3SCameron Grant 	if (codec == NULL)
9129ec437a3SCameron Grant 		return -1;
9139ec437a3SCameron Grant 	return ac97_reinitmixer(codec);
9149ec437a3SCameron Grant }
9159ec437a3SCameron Grant 
9169ec437a3SCameron Grant static int
91766ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
918987e5972SCameron Grant {
919987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
920341f16ccSCameron Grant 
92139004e69SCameron Grant 	if (codec == NULL)
92239004e69SCameron Grant 		return -1;
923987e5972SCameron Grant 	return ac97_setmixer(codec, dev, left, right);
924987e5972SCameron Grant }
925987e5972SCameron Grant 
926987e5972SCameron Grant static int
92766ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
928987e5972SCameron Grant {
929987e5972SCameron Grant 	int i;
930987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
931341f16ccSCameron Grant 
93239004e69SCameron Grant 	if (codec == NULL)
93339004e69SCameron Grant 		return -1;
934987e5972SCameron Grant 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
93539004e69SCameron Grant 		if ((src & (1 << i)) != 0)
93639004e69SCameron Grant 			break;
937987e5972SCameron Grant 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
938987e5972SCameron Grant }
939987e5972SCameron Grant 
9400f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = {
9410f55ac6cSCameron Grant     	KOBJMETHOD(mixer_init,		ac97mix_init),
9420f55ac6cSCameron Grant     	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
9430f55ac6cSCameron Grant     	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
9440f55ac6cSCameron Grant     	KOBJMETHOD(mixer_set,		ac97mix_set),
9450f55ac6cSCameron Grant     	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
9460f55ac6cSCameron Grant 	{ 0, 0 }
947987e5972SCameron Grant };
9480f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer);
9490f55ac6cSCameron Grant 
9500f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
9510f55ac6cSCameron Grant 
9520f55ac6cSCameron Grant kobj_class_t
9530f55ac6cSCameron Grant ac97_getmixerclass(void)
9540f55ac6cSCameron Grant {
9550f55ac6cSCameron Grant 	return &ac97mixer_class;
9560f55ac6cSCameron Grant }
9570f55ac6cSCameron Grant 
958987e5972SCameron Grant 
959