xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision 90da2b2859b259fca1ab223931f549a72e21a3a5)
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 
2790da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS
2890da2b28SAriff Abdullah #include "opt_snd.h"
2990da2b28SAriff Abdullah #endif
3090da2b28SAriff Abdullah 
31ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
32ef9308b1SCameron Grant #include <dev/sound/pcm/ac97.h>
33f9eb1409SOrion Hodson #include <dev/sound/pcm/ac97_patch.h>
34987e5972SCameron Grant 
35a580b31aSAriff Abdullah #include <dev/pci/pcivar.h>
36a580b31aSAriff Abdullah 
370f55ac6cSCameron Grant #include "mixer_if.h"
38987e5972SCameron Grant 
3967b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$");
4067b1dce3SCameron Grant 
410f55ac6cSCameron Grant MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
42987e5972SCameron Grant 
4379bb7d52SCameron Grant struct ac97mixtable_entry {
4483234531SAriff Abdullah 	int reg;		/* register index		*/
4596524a52SOrion Hodson 				/* reg < 0 if inverted polarity	*/
4696524a52SOrion Hodson 	unsigned bits:4;	/* width of control field	*/
4796524a52SOrion Hodson 	unsigned ofs:4;		/* offset (only if stereo=0)	*/
4896524a52SOrion Hodson 	unsigned stereo:1;	/* set for stereo controls	*/
4996524a52SOrion Hodson 	unsigned mute:1;	/* bit15 is MUTE		*/
5096524a52SOrion Hodson 	unsigned recidx:4;	/* index in rec mux		*/
5196524a52SOrion Hodson 	unsigned mask:1;	/* use only masked bits		*/
5296524a52SOrion Hodson 	unsigned enable:1;	/* entry is enabled		*/
5379bb7d52SCameron Grant };
5479bb7d52SCameron Grant 
5569f6d261SAriff Abdullah #define AC97_MIXER_SIZE		SOUND_MIXER_NRDEVICES
5669f6d261SAriff Abdullah 
5766ef8af5SCameron Grant struct ac97_info {
5866ef8af5SCameron Grant 	kobj_t methods;
5966ef8af5SCameron Grant 	device_t dev;
6066ef8af5SCameron Grant 	void *devinfo;
6119921b23SOrion Hodson 	u_int32_t id;
62fd7390d6SAriff Abdullah 	u_int32_t subvendor;
6366ef8af5SCameron Grant 	unsigned count, caps, se, extcaps, extid, extstat, noext:1;
6479bb7d52SCameron Grant 	u_int32_t flags;
6569f6d261SAriff Abdullah 	struct ac97mixtable_entry mix[AC97_MIXER_SIZE];
6669f6d261SAriff Abdullah 	char name[16];
6700acb133SCameron Grant 	struct mtx *lock;
6866ef8af5SCameron Grant };
6966ef8af5SCameron Grant 
7019921b23SOrion Hodson struct ac97_vendorid {
7119921b23SOrion Hodson 	u_int32_t   id;
7219921b23SOrion Hodson 	const char *name;
7319921b23SOrion Hodson };
7419921b23SOrion Hodson 
75987e5972SCameron Grant struct ac97_codecid {
7619921b23SOrion Hodson 	u_int32_t  id;
7719921b23SOrion Hodson 	u_int8_t   stepmask;
7819921b23SOrion Hodson 	u_int8_t   noext:1;
79987e5972SCameron Grant 	char 	  *name;
80f9eb1409SOrion Hodson 	ac97_patch patch;
81987e5972SCameron Grant };
82987e5972SCameron Grant 
8369f6d261SAriff Abdullah static const struct ac97mixtable_entry ac97mixtable_default[AC97_MIXER_SIZE] = {
8496524a52SOrion Hodson     /*	[offset]			reg	     bits of st mu re mk en */
85341f16ccSCameron Grant 	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0, 1 },
86a52604cfSOrion Hodson 	[SOUND_MIXER_OGAIN]	= { AC97_MIX_AUXOUT, 	5, 0, 1, 1, 0, 0, 0 },
87341f16ccSCameron Grant 	[SOUND_MIXER_PHONEOUT]	= { AC97_MIX_MONO, 	5, 0, 0, 1, 7, 0, 0 },
88341f16ccSCameron Grant 	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1, 0 },
89341f16ccSCameron Grant 	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1, 0 },
90341f16ccSCameron Grant 	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0, 1 },
91341f16ccSCameron Grant 	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0, 0 },
92341f16ccSCameron Grant 	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0, 1 },
93341f16ccSCameron Grant 	[SOUND_MIXER_PHONEIN]	= { AC97_MIX_PHONE, 	5, 0, 0, 1, 8, 0, 0 },
9496524a52SOrion Hodson 	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 1, 1 },
9596524a52SOrion Hodson 	/* use igain for the mic 20dB boost */
9696524a52SOrion Hodson 	[SOUND_MIXER_IGAIN] 	= { -AC97_MIX_MIC, 	1, 6, 0, 0, 0, 1, 1 },
97341f16ccSCameron Grant 	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
98341f16ccSCameron Grant 	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
99341f16ccSCameron Grant 	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
100341f16ccSCameron Grant 	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
101987e5972SCameron Grant };
102987e5972SCameron Grant 
10319921b23SOrion Hodson static const struct ac97_vendorid ac97vendorid[] = {
10419921b23SOrion Hodson 	{ 0x41445300, "Analog Devices" },
10519921b23SOrion Hodson 	{ 0x414b4d00, "Asahi Kasei" },
10619921b23SOrion Hodson 	{ 0x414c4300, "Realtek" },
10719921b23SOrion Hodson 	{ 0x414c4700, "Avance Logic" },
10819921b23SOrion Hodson 	{ 0x43525900, "Cirrus Logic" },
10919921b23SOrion Hodson 	{ 0x434d4900, "C-Media Electronics" },
11019921b23SOrion Hodson 	{ 0x43585400, "Conexant" },
1113c6b655dSMathew Kanner 	{ 0x44543000, "Diamond Technology" },
11261a0da1dSOrion Hodson 	{ 0x454d4300, "eMicro" },
11319921b23SOrion Hodson 	{ 0x45838300, "ESS Technology" },
1143c6b655dSMathew Kanner 	{ 0x48525300, "Intersil" },
11519921b23SOrion Hodson 	{ 0x49434500, "ICEnsemble" },
1163c6b655dSMathew Kanner 	{ 0x49544500, "ITE, Inc." },
11719921b23SOrion Hodson 	{ 0x4e534300, "National Semiconductor" },
11819921b23SOrion Hodson 	{ 0x50534300, "Philips Semiconductor" },
11919921b23SOrion Hodson 	{ 0x83847600, "SigmaTel" },
1203c6b655dSMathew Kanner 	{ 0x53494c00, "Silicon Laboratories" },
12119921b23SOrion Hodson 	{ 0x54524100, "TriTech" },
1223c6b655dSMathew Kanner 	{ 0x54584e00, "Texas Instruments" },
12319921b23SOrion Hodson 	{ 0x56494100, "VIA Technologies" },
1243c6b655dSMathew Kanner 	{ 0x57454300, "Winbond" },
12519921b23SOrion Hodson 	{ 0x574d4c00, "Wolfson" },
12619921b23SOrion Hodson 	{ 0x594d4800, "Yamaha" },
12786336196SAlexander Leidinger 	/*
12886336196SAlexander Leidinger 	 * XXX This is a fluke, really! The real vendor
12986336196SAlexander Leidinger 	 * should be SigmaTel, not this! This should be
13086336196SAlexander Leidinger 	 * removed someday!
13186336196SAlexander Leidinger 	 */
1326f0182bdSOrion Hodson 	{ 0x01408300, "Creative" },
13319921b23SOrion Hodson 	{ 0x00000000, NULL }
13419921b23SOrion Hodson };
13519921b23SOrion Hodson 
136987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = {
13719921b23SOrion Hodson 	{ 0x41445303, 0x00, 0, "AD1819",	0 },
13819921b23SOrion Hodson 	{ 0x41445340, 0x00, 0, "AD1881",	0 },
13919921b23SOrion Hodson 	{ 0x41445348, 0x00, 0, "AD1881A",	0 },
14019921b23SOrion Hodson 	{ 0x41445360, 0x00, 0, "AD1885",	0 },
14119921b23SOrion Hodson 	{ 0x41445361, 0x00, 0, "AD1886", 	ad1886_patch },
142ba548c64SOrion Hodson 	{ 0x41445362, 0x00, 0, "AD1887", 	0 },
143ba548c64SOrion Hodson 	{ 0x41445363, 0x00, 0, "AD1886A", 	0 },
144c5cb8d60SScott Long 	{ 0x41445368, 0x00, 0, "AD1888", 	ad198x_patch },
145a52604cfSOrion Hodson 	{ 0x41445370, 0x00, 0, "AD1980",	ad198x_patch },
146ba548c64SOrion Hodson 	{ 0x41445372, 0x00, 0, "AD1981A",	0 },
1477699548fSAriff Abdullah 	{ 0x41445374, 0x00, 0, "AD1981B",	ad1981b_patch },
148a52604cfSOrion Hodson 	{ 0x41445375, 0x00, 0, "AD1985",	ad198x_patch },
149e1e05d5dSAriff Abdullah 	{ 0x41445378, 0x00, 0, "AD1986",	ad198x_patch },
15019921b23SOrion Hodson 	{ 0x414b4d00, 0x00, 1, "AK4540", 	0 },
15119921b23SOrion Hodson 	{ 0x414b4d01, 0x00, 1, "AK4542", 	0 },
15219921b23SOrion Hodson 	{ 0x414b4d02, 0x00, 1, "AK4543", 	0 },
1533c6b655dSMathew Kanner 	{ 0x414b4d06, 0x00, 0, "AK4544A",	0 },
1543c6b655dSMathew Kanner 	{ 0x454b4d07, 0x00, 0, "AK4545",	0 },
15519921b23SOrion Hodson 	{ 0x414c4320, 0x0f, 0, "ALC100",	0 },
1569963235aSOrion Hodson 	{ 0x414c4730, 0x0f, 0, "ALC101",	0 },
15719921b23SOrion Hodson 	{ 0x414c4710, 0x0f, 0, "ALC200", 	0 },
15819921b23SOrion Hodson 	{ 0x414c4740, 0x0f, 0, "ALC202", 	0 },
15919921b23SOrion Hodson 	{ 0x414c4720, 0x0f, 0, "ALC650", 	0 },
160b327ee51SAriff Abdullah 	{ 0x414c4752, 0x0f, 0, "ALC250",	0 },
161fd7390d6SAriff Abdullah 	{ 0x414c4760, 0x0f, 0, "ALC655",	alc655_patch },
162b7994e34SPyun YongHyeon 	{ 0x414c4770, 0x0f, 0, "ALC203",	0 },
16394ed763dSJun Kuriyama 	{ 0x414c4780, 0x0f, 0, "ALC658",	0 },
1643c6b655dSMathew Kanner 	{ 0x414c4790, 0x0f, 0, "ALC850",	0 },
16519921b23SOrion Hodson 	{ 0x43525900, 0x07, 0, "CS4297", 	0 },
16619921b23SOrion Hodson 	{ 0x43525910, 0x07, 0, "CS4297A", 	0 },
16719921b23SOrion Hodson 	{ 0x43525920, 0x07, 0, "CS4294/98",	0 },
168e5728f83SMIHIRA Sanpei Yoshiro 	{ 0x4352592d, 0x07, 0, "CS4294",	0 },
16919921b23SOrion Hodson 	{ 0x43525930, 0x07, 0, "CS4299",	0 },
17019921b23SOrion Hodson 	{ 0x43525940, 0x07, 0, "CS4201",	0 },
171b2a0f525SOrion Hodson 	{ 0x43525958, 0x07, 0, "CS4205",	0 },
17219921b23SOrion Hodson 	{ 0x43525960, 0x07, 0, "CS4291A",	0 },
173cb44f623SAlexander Leidinger 	{ 0x434d4961, 0x00, 0, "CMI9739",	cmi9739_patch },
17419921b23SOrion Hodson 	{ 0x434d4941, 0x00, 0, "CMI9738",	0 },
175cb44f623SAlexander Leidinger 	{ 0x434d4978, 0x00, 0, "CMI9761",	0 },
176cb44f623SAlexander Leidinger 	{ 0x434d4982, 0x00, 0, "CMI9761",	0 },
177cb44f623SAlexander Leidinger 	{ 0x434d4983, 0x00, 0, "CMI9761",	0 },
1783c6b655dSMathew Kanner 	{ 0x43585421, 0x00, 0, "HSD11246",	0 },
1793c6b655dSMathew Kanner 	{ 0x43585428, 0x07, 0, "CX20468",	0 },
18080138937SAriff Abdullah 	{ 0x43585430, 0x00, 0, "CX20468-21",	0 },
1813c6b655dSMathew Kanner 	{ 0x44543000, 0x00, 0, "DT0398",	0 },
18261a0da1dSOrion Hodson 	{ 0x454d4323, 0x00, 0, "EM28023",	0 },
18361a0da1dSOrion Hodson 	{ 0x454d4328, 0x00, 0, "EM28028",	0 },
18419921b23SOrion Hodson 	{ 0x45838308, 0x00, 0, "ES1988",	0 }, /* Formerly ES1921(?) */
1853c6b655dSMathew Kanner 	{ 0x48525300, 0x00, 0, "HMP9701",	0 },
18619921b23SOrion Hodson 	{ 0x49434501, 0x00, 0, "ICE1230",	0 },
18719921b23SOrion Hodson 	{ 0x49434511, 0x00, 0, "ICE1232",	0 },
18819921b23SOrion Hodson 	{ 0x49434514, 0x00, 0, "ICE1232A",	0 },
18949fd6905SOrion Hodson 	{ 0x49434551, 0x03, 0, "VT1616",	0 }, /* Via badged ICE */
1903c6b655dSMathew Kanner 	{ 0x49544520, 0x00, 0, "ITE2226E",	0 },
1913c6b655dSMathew Kanner 	{ 0x49544560, 0x07, 0, "ITE2646E",	0 }, /* XXX: patch needed */
19219921b23SOrion Hodson 	{ 0x4e534340, 0x00, 0, "LM4540",	0 }, /* Spec blank on revid */
19319921b23SOrion Hodson 	{ 0x4e534343, 0x00, 0, "LM4543",	0 }, /* Ditto */
19419921b23SOrion Hodson 	{ 0x4e534346, 0x00, 0, "LM4546A",	0 },
19519921b23SOrion Hodson 	{ 0x4e534348, 0x00, 0, "LM4548A",	0 },
1963c6b655dSMathew Kanner 	{ 0x4e534331, 0x00, 0, "LM4549",	0 },
19719921b23SOrion Hodson 	{ 0x4e534349, 0x00, 0, "LM4549A",	0 },
19819921b23SOrion Hodson 	{ 0x4e534350, 0x00, 0, "LM4550",	0 },
19919921b23SOrion Hodson 	{ 0x50534301, 0x00, 0, "UCB1510",	0 },
20019921b23SOrion Hodson 	{ 0x50534304, 0x00, 0, "UCB1400",	0 },
20119921b23SOrion Hodson 	{ 0x83847600, 0x00, 0, "STAC9700/83/84",	0 },
20219921b23SOrion Hodson 	{ 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
20319921b23SOrion Hodson 	{ 0x83847605, 0x00, 0, "STAC9704",	0 },
20419921b23SOrion Hodson 	{ 0x83847608, 0x00, 0, "STAC9708/11",	0 },
20519921b23SOrion Hodson 	{ 0x83847609, 0x00, 0, "STAC9721/23",	0 },
20619921b23SOrion Hodson 	{ 0x83847644, 0x00, 0, "STAC9744/45",	0 },
20719921b23SOrion Hodson 	{ 0x83847650, 0x00, 0, "STAC9750/51",	0 },
20819921b23SOrion Hodson 	{ 0x83847652, 0x00, 0, "STAC9752/53",	0 },
20919921b23SOrion Hodson 	{ 0x83847656, 0x00, 0, "STAC9756/57",	0 },
21019921b23SOrion Hodson 	{ 0x83847658, 0x00, 0, "STAC9758/59",	0 },
21119921b23SOrion Hodson 	{ 0x83847660, 0x00, 0, "STAC9760/61",	0 }, /* Extrapolated */
21219921b23SOrion Hodson 	{ 0x83847662, 0x00, 0, "STAC9762/63",	0 }, /* Extrapolated */
213cb44f623SAlexander Leidinger 	{ 0x83847666, 0x00, 0, "STAC9766/67",	0 },
21419921b23SOrion Hodson 	{ 0x53494c22, 0x00, 0, "Si3036",	0 },
21519921b23SOrion Hodson 	{ 0x53494c23, 0x00, 0, "Si3038",	0 },
21619921b23SOrion Hodson 	{ 0x54524103, 0x00, 0, "TR28023",	0 }, /* Extrapolated */
21719921b23SOrion Hodson 	{ 0x54524106, 0x00, 0, "TR28026",	0 },
21819921b23SOrion Hodson 	{ 0x54524108, 0x00, 0, "TR28028",	0 },
21919921b23SOrion Hodson 	{ 0x54524123, 0x00, 0, "TR28602",	0 },
2203c6b655dSMathew Kanner 	{ 0x54524e03, 0x07, 0, "TLV320AIC27",	0 },
2213c6b655dSMathew Kanner 	{ 0x54584e20, 0x00, 0, "TLC320AD90",	0 },
22219921b23SOrion Hodson 	{ 0x56494161, 0x00, 0, "VIA1612A",      0 },
22380138937SAriff Abdullah 	{ 0x56494170, 0x00, 0, "VIA1617A",      0 },
22419921b23SOrion Hodson 	{ 0x574d4c00, 0x00, 0, "WM9701A",	0 },
22519921b23SOrion Hodson 	{ 0x574d4c03, 0x00, 0, "WM9703/4/7/8",	0 },
22619921b23SOrion Hodson 	{ 0x574d4c04, 0x00, 0, "WM9704Q",	0 },
22719921b23SOrion Hodson 	{ 0x574d4c05, 0x00, 0, "WM9705/10",	0 },
2283c6b655dSMathew Kanner 	{ 0x574d4d09, 0x00, 0, "WM9709",	0 },
2293c6b655dSMathew Kanner 	{ 0x574d4c12, 0x00, 0, "WM9711/12",	0 }, /* XXX: patch needed */
2303c6b655dSMathew Kanner 	{ 0x57454301, 0x00, 0, "W83971D",	0 },
23119921b23SOrion Hodson 	{ 0x594d4800, 0x00, 0, "YMF743",	0 },
23219921b23SOrion Hodson 	{ 0x594d4802, 0x00, 0, "YMF752",	0 },
23319921b23SOrion Hodson 	{ 0x594d4803, 0x00, 0, "YMF753",	0 },
23486336196SAlexander Leidinger 	/*
23586336196SAlexander Leidinger 	 * XXX This is a fluke, really! The real codec
23686336196SAlexander Leidinger 	 * should be STAC9704, not this! This should be
23786336196SAlexander Leidinger 	 * removed someday!
23886336196SAlexander Leidinger 	 */
2396f0182bdSOrion Hodson 	{ 0x01408384, 0x00, 0, "EV1938",	0 },
24019921b23SOrion Hodson 	{ 0, 0, 0, NULL, 0 }
241987e5972SCameron Grant };
242987e5972SCameron Grant 
243987e5972SCameron Grant static char *ac97enhancement[] = {
24404553e63SCameron Grant 	"no 3D Stereo Enhancement",
245987e5972SCameron Grant 	"Analog Devices Phat Stereo",
246987e5972SCameron Grant 	"Creative Stereo Enhancement",
247987e5972SCameron Grant 	"National Semi 3D Stereo Enhancement",
248987e5972SCameron Grant 	"Yamaha Ymersion",
249987e5972SCameron Grant 	"BBE 3D Stereo Enhancement",
250987e5972SCameron Grant 	"Crystal Semi 3D Stereo Enhancement",
251987e5972SCameron Grant 	"Qsound QXpander",
252987e5972SCameron Grant 	"Spatializer 3D Stereo Enhancement",
253987e5972SCameron Grant 	"SRS 3D Stereo Enhancement",
254987e5972SCameron Grant 	"Platform Tech 3D Stereo Enhancement",
255987e5972SCameron Grant 	"AKM 3D Audio",
256987e5972SCameron Grant 	"Aureal Stereo Enhancement",
257987e5972SCameron Grant 	"Aztech 3D Enhancement",
258987e5972SCameron Grant 	"Binaura 3D Audio Enhancement",
259987e5972SCameron Grant 	"ESS Technology Stereo Enhancement",
260987e5972SCameron Grant 	"Harman International VMAx",
261987e5972SCameron Grant 	"Nvidea 3D Stereo Enhancement",
262987e5972SCameron Grant 	"Philips Incredible Sound",
263987e5972SCameron Grant 	"Texas Instruments 3D Stereo Enhancement",
264987e5972SCameron Grant 	"VLSI Technology 3D Stereo Enhancement",
265987e5972SCameron Grant 	"TriTech 3D Stereo Enhancement",
266987e5972SCameron Grant 	"Realtek 3D Stereo Enhancement",
267987e5972SCameron Grant 	"Samsung 3D Stereo Enhancement",
268987e5972SCameron Grant 	"Wolfson Microelectronics 3D Enhancement",
269987e5972SCameron Grant 	"Delta Integration 3D Enhancement",
270987e5972SCameron Grant 	"SigmaTel 3D Enhancement",
271987e5972SCameron Grant 	"Reserved 27",
272987e5972SCameron Grant 	"Rockwell 3D Stereo Enhancement",
273987e5972SCameron Grant 	"Reserved 29",
274987e5972SCameron Grant 	"Reserved 30",
275987e5972SCameron Grant 	"Reserved 31"
276987e5972SCameron Grant };
277987e5972SCameron Grant 
278987e5972SCameron Grant static char *ac97feature[] = {
279987e5972SCameron Grant 	"mic channel",
280987e5972SCameron Grant 	"reserved",
281987e5972SCameron Grant 	"tone",
282987e5972SCameron Grant 	"simulated stereo",
283987e5972SCameron Grant 	"headphone",
284987e5972SCameron Grant 	"bass boost",
285987e5972SCameron Grant 	"18 bit DAC",
286987e5972SCameron Grant 	"20 bit DAC",
287987e5972SCameron Grant 	"18 bit ADC",
288987e5972SCameron Grant 	"20 bit ADC"
289987e5972SCameron Grant };
290987e5972SCameron Grant 
29139004e69SCameron Grant static char *ac97extfeature[] = {
29239004e69SCameron Grant 	"variable rate PCM",
29339004e69SCameron Grant 	"double rate PCM",
29439004e69SCameron Grant 	"reserved 1",
29539004e69SCameron Grant 	"variable rate mic",
29639004e69SCameron Grant 	"reserved 2",
29739004e69SCameron Grant 	"reserved 3",
29839004e69SCameron Grant 	"center DAC",
29939004e69SCameron Grant 	"surround DAC",
30039004e69SCameron Grant 	"LFE DAC",
30139004e69SCameron Grant 	"AMAP",
30239004e69SCameron Grant 	"reserved 4",
30339004e69SCameron Grant 	"reserved 5",
30439004e69SCameron Grant 	"reserved 6",
30539004e69SCameron Grant 	"reserved 7",
30639004e69SCameron Grant };
30739004e69SCameron Grant 
308f9eb1409SOrion Hodson u_int16_t
309f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg)
31039004e69SCameron Grant {
31186336196SAlexander Leidinger 	if (codec->flags & AC97_F_RDCD_BUG) {
31286336196SAlexander Leidinger 		u_int16_t i[2], j = 100;
31386336196SAlexander Leidinger 
31486336196SAlexander Leidinger 		i[0] = AC97_READ(codec->methods, codec->devinfo, reg);
31586336196SAlexander Leidinger 		i[1] = AC97_READ(codec->methods, codec->devinfo, reg);
31686336196SAlexander Leidinger 		while (i[0] != i[1] && j)
31786336196SAlexander Leidinger 			i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg);
31886336196SAlexander Leidinger #if 0
31986336196SAlexander Leidinger 		if (j < 100) {
32086336196SAlexander Leidinger 			device_printf(codec->dev, "%s(): Inconsistent register value at"
32186336196SAlexander Leidinger 					" 0x%08x (retry: %d)\n", __func__, reg, 100 - j);
32286336196SAlexander Leidinger 		}
32386336196SAlexander Leidinger #endif
32486336196SAlexander Leidinger 		return i[!(j & 1)];
32586336196SAlexander Leidinger 	}
3260f55ac6cSCameron Grant 	return AC97_READ(codec->methods, codec->devinfo, reg);
32739004e69SCameron Grant }
32839004e69SCameron Grant 
329f9eb1409SOrion Hodson void
330f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
33139004e69SCameron Grant {
3320f55ac6cSCameron Grant 	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
33339004e69SCameron Grant }
33439004e69SCameron Grant 
335c6d4b83aSOrion Hodson static void
336c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec)
337c6d4b83aSOrion Hodson {
338c6d4b83aSOrion Hodson 	u_int32_t i, ps;
339f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_RESET, 0);
340c6d4b83aSOrion Hodson 	for (i = 0; i < 500; i++) {
341f9eb1409SOrion Hodson 		ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
342c6d4b83aSOrion Hodson 		if (ps == AC97_POWER_STATUS)
343c6d4b83aSOrion Hodson 			return;
344c6d4b83aSOrion Hodson 		DELAY(1000);
345c6d4b83aSOrion Hodson 	}
346a825c6e5SOrion Hodson 	device_printf(codec->dev, "AC97 reset timed out.\n");
347c6d4b83aSOrion Hodson }
348c6d4b83aSOrion Hodson 
34939004e69SCameron Grant int
35039004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate)
35139004e69SCameron Grant {
35239004e69SCameron Grant 	u_int16_t v;
35339004e69SCameron Grant 
35439004e69SCameron Grant 	switch(which) {
35539004e69SCameron Grant 	case AC97_REGEXT_FDACRATE:
35639004e69SCameron Grant 	case AC97_REGEXT_SDACRATE:
35739004e69SCameron Grant 	case AC97_REGEXT_LDACRATE:
35839004e69SCameron Grant 	case AC97_REGEXT_LADCRATE:
35939004e69SCameron Grant 	case AC97_REGEXT_MADCRATE:
36039004e69SCameron Grant 		break;
36139004e69SCameron Grant 
36239004e69SCameron Grant 	default:
36339004e69SCameron Grant 		return -1;
36439004e69SCameron Grant 	}
36539004e69SCameron Grant 
36666ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
36739004e69SCameron Grant 	if (rate != 0) {
36839004e69SCameron Grant 		v = rate;
36939004e69SCameron Grant 		if (codec->extstat & AC97_EXTCAP_DRA)
37039004e69SCameron Grant 			v >>= 1;
371f9eb1409SOrion Hodson 		ac97_wrcd(codec, which, v);
37239004e69SCameron Grant 	}
373f9eb1409SOrion Hodson 	v = ac97_rdcd(codec, which);
37439004e69SCameron Grant 	if (codec->extstat & AC97_EXTCAP_DRA)
37539004e69SCameron Grant 		v <<= 1;
37666ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
37739004e69SCameron Grant 	return v;
37839004e69SCameron Grant }
37939004e69SCameron Grant 
38039004e69SCameron Grant int
38139004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
38239004e69SCameron Grant {
38339004e69SCameron Grant 	mode &= AC97_EXTCAPS;
384647fbfebSOrion Hodson 	if ((mode & ~codec->extcaps) != 0) {
385647fbfebSOrion Hodson 		device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
386647fbfebSOrion Hodson 			      mode);
38739004e69SCameron Grant 		return -1;
388647fbfebSOrion Hodson 	}
38966ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
390f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
391f9eb1409SOrion Hodson 	codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
39266ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
39339004e69SCameron Grant 	return (mode == codec->extstat)? 0 : -1;
39439004e69SCameron Grant }
39539004e69SCameron Grant 
3969ec437a3SCameron Grant u_int16_t
3979ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec)
3989ec437a3SCameron Grant {
3999ec437a3SCameron Grant 	return codec->extstat;
4009ec437a3SCameron Grant }
4019ec437a3SCameron Grant 
4029ec437a3SCameron Grant u_int16_t
4039ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec)
4049ec437a3SCameron Grant {
4059ec437a3SCameron Grant 	return codec->extcaps;
4069ec437a3SCameron Grant }
4079ec437a3SCameron Grant 
4085d91ad67SCameron Grant u_int16_t
4095d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec)
4105d91ad67SCameron Grant {
4115d91ad67SCameron Grant 	return codec->caps;
4125d91ad67SCameron Grant }
4135d91ad67SCameron Grant 
414fd7390d6SAriff Abdullah u_int32_t
415fd7390d6SAriff Abdullah ac97_getsubvendor(struct ac97_info *codec)
416fd7390d6SAriff Abdullah {
417fd7390d6SAriff Abdullah 	return codec->subvendor;
418fd7390d6SAriff Abdullah }
419fd7390d6SAriff Abdullah 
420987e5972SCameron Grant static int
421987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel)
422987e5972SCameron Grant {
423987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
424341f16ccSCameron Grant 
425987e5972SCameron Grant 	if (e->recidx > 0) {
426987e5972SCameron Grant 		int val = e->recidx - 1;
427987e5972SCameron Grant 		val |= val << 8;
42866ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
429f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REG_RECSEL, val);
43066ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
431987e5972SCameron Grant 		return 0;
43239004e69SCameron Grant 	} else
43339004e69SCameron Grant 		return -1;
434987e5972SCameron Grant }
435987e5972SCameron Grant 
436987e5972SCameron Grant static int
437987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
438987e5972SCameron Grant {
439987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
440341f16ccSCameron Grant 
441341f16ccSCameron Grant 	if (e->reg && e->enable && e->bits) {
44296524a52SOrion Hodson 		int mask, max, val, reg;
44396524a52SOrion Hodson 
44496524a52SOrion Hodson 		reg = (e->reg >= 0) ? e->reg : -e->reg;	/* AC97 register    */
44596524a52SOrion Hodson 		max = (1 << e->bits) - 1;		/* actual range	    */
44696524a52SOrion Hodson 		mask = (max << 8) | max;		/* bits of interest */
447987e5972SCameron Grant 
44839004e69SCameron Grant 		if (!e->stereo)
44939004e69SCameron Grant 			right = left;
45096524a52SOrion Hodson 
45196524a52SOrion Hodson 		/*
45296524a52SOrion Hodson 		 * Invert the range if the polarity requires so,
45396524a52SOrion Hodson 		 * then scale to 0..max-1 to compute the value to
45496524a52SOrion Hodson 		 * write into the codec, and scale back to 0..100
45596524a52SOrion Hodson 		 * for the return value.
45696524a52SOrion Hodson 		 */
457987e5972SCameron Grant 		if (e->reg > 0) {
458987e5972SCameron Grant 			left = 100 - left;
459987e5972SCameron Grant 			right = 100 - right;
460987e5972SCameron Grant 		}
461987e5972SCameron Grant 
462987e5972SCameron Grant 		left = (left * max) / 100;
463987e5972SCameron Grant 		right = (right * max) / 100;
464987e5972SCameron Grant 
465987e5972SCameron Grant 		val = (left << 8) | right;
466987e5972SCameron Grant 
467987e5972SCameron Grant 		left = (left * 100) / max;
468987e5972SCameron Grant 		right = (right * 100) / max;
469987e5972SCameron Grant 
470987e5972SCameron Grant 		if (e->reg > 0) {
471987e5972SCameron Grant 			left = 100 - left;
472987e5972SCameron Grant 			right = 100 - right;
473987e5972SCameron Grant 		}
474987e5972SCameron Grant 
47596524a52SOrion Hodson 		/*
47696524a52SOrion Hodson 		 * For mono controls, trim val and mask, also taking
47796524a52SOrion Hodson 		 * care of e->ofs (offset of control field).
47896524a52SOrion Hodson 		 */
47996524a52SOrion Hodson 		if (e->ofs) {
480987e5972SCameron Grant 			val &= max;
481987e5972SCameron Grant 			val <<= e->ofs;
48296524a52SOrion Hodson 			mask = (max << e->ofs);
48396524a52SOrion Hodson 		}
48496524a52SOrion Hodson 
48596524a52SOrion Hodson 		/*
48696524a52SOrion Hodson 		 * If we have a mute bit, add it to the mask and
48796524a52SOrion Hodson 		 * update val and set mute if both channels require a
48896524a52SOrion Hodson 		 * zero volume.
48996524a52SOrion Hodson 		 */
49096524a52SOrion Hodson 		if (e->mute == 1) {
49196524a52SOrion Hodson 			mask |= AC97_MUTE;
49296524a52SOrion Hodson 			if (left == 0 && right == 0)
49396524a52SOrion Hodson 				val = AC97_MUTE;
49496524a52SOrion Hodson 		}
49596524a52SOrion Hodson 
49696524a52SOrion Hodson 		/*
49796524a52SOrion Hodson 		 * If the mask bit is set, do not alter the other bits.
49896524a52SOrion Hodson 		 */
49996524a52SOrion Hodson 		snd_mtxlock(codec->lock);
500987e5972SCameron Grant 		if (e->mask) {
50186336196SAlexander Leidinger 			int cur = ac97_rdcd(codec, reg);
50296524a52SOrion Hodson 			val |= cur & ~(mask);
503987e5972SCameron Grant 		}
504f9eb1409SOrion Hodson 		ac97_wrcd(codec, reg, val);
50566ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
506987e5972SCameron Grant 		return left | (right << 8);
507341f16ccSCameron Grant 	} else {
50886336196SAlexander Leidinger #if 0
50986336196SAlexander Leidinger 		printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable);
51086336196SAlexander Leidinger #endif
51139004e69SCameron Grant 		return -1;
512987e5972SCameron Grant 	}
513341f16ccSCameron Grant }
514987e5972SCameron Grant 
515108082c4SOrion Hodson static void
516108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec)
517108082c4SOrion Hodson {
518cfd5696dSOrion Hodson 	int keep_ogain;
519cfd5696dSOrion Hodson 
520a52604cfSOrion Hodson 	/*
521cfd5696dSOrion Hodson 	 * By default, The ac97 aux_out register (0x04) corresponds to OSS's
522cfd5696dSOrion Hodson 	 * OGAIN setting.
523a52604cfSOrion Hodson 	 *
524cfd5696dSOrion Hodson 	 * We first check whether aux_out is a valid register.  If not
525cfd5696dSOrion Hodson 	 * we may not want to keep ogain.
526a52604cfSOrion Hodson 	 */
527cfd5696dSOrion Hodson 	keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000;
528a52604cfSOrion Hodson 
529a52604cfSOrion Hodson 	/*
530a52604cfSOrion Hodson 	 * Determine what AUX_OUT really means, it can be:
531108082c4SOrion Hodson 	 *
532108082c4SOrion Hodson 	 * 1. Headphone out.
533108082c4SOrion Hodson 	 * 2. 4-Channel Out
534108082c4SOrion Hodson 	 * 3. True line level out (effectively master volume).
535108082c4SOrion Hodson 	 *
536108082c4SOrion Hodson 	 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
537108082c4SOrion Hodson 	 */
538a52604cfSOrion Hodson 	if (codec->extcaps & AC97_EXTCAP_SDAC &&
539f9eb1409SOrion Hodson 	    ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
540cfd5696dSOrion Hodson 		codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND;
541cfd5696dSOrion Hodson 		keep_ogain = 1;
542cfd5696dSOrion Hodson 	}
543cfd5696dSOrion Hodson 
544cfd5696dSOrion Hodson 	if (keep_ogain == 0) {
545cfd5696dSOrion Hodson 		bzero(&codec->mix[SOUND_MIXER_OGAIN],
546cfd5696dSOrion Hodson 		      sizeof(codec->mix[SOUND_MIXER_OGAIN]));
547108082c4SOrion Hodson 	}
548a52604cfSOrion Hodson }
549a52604cfSOrion Hodson 
550a52604cfSOrion Hodson static void
551a52604cfSOrion Hodson ac97_fix_tone(struct ac97_info *codec)
552a52604cfSOrion Hodson {
553f0c4d272SAriff Abdullah 	/*
554f0c4d272SAriff Abdullah 	 * YMF chips does not indicate tone and 3D enhancement capability
555f0c4d272SAriff Abdullah 	 * in the AC97_REG_RESET register.
556f0c4d272SAriff Abdullah 	 */
557f0c4d272SAriff Abdullah 	switch (codec->id) {
558f0c4d272SAriff Abdullah 	case 0x594d4800:	/* YMF743 */
559f0c4d272SAriff Abdullah 	case 0x594d4803:	/* YMF753 */
560f0c4d272SAriff Abdullah 		codec->caps |= AC97_CAP_TONE;
561f0c4d272SAriff Abdullah 		codec->se |= 0x04;
562f0c4d272SAriff Abdullah 		break;
563f0c4d272SAriff Abdullah 	case 0x594d4802:	/* YMF752 */
564f0c4d272SAriff Abdullah 		codec->se |= 0x04;
565f0c4d272SAriff Abdullah 		break;
566f0c4d272SAriff Abdullah 	default:
567f0c4d272SAriff Abdullah 		break;
568f0c4d272SAriff Abdullah 	}
569f0c4d272SAriff Abdullah 
570a52604cfSOrion Hodson 	/* Hide treble and bass if they don't exist */
571a52604cfSOrion Hodson 	if ((codec->caps & AC97_CAP_TONE) == 0) {
572a52604cfSOrion Hodson 		bzero(&codec->mix[SOUND_MIXER_BASS],
573a52604cfSOrion Hodson 		      sizeof(codec->mix[SOUND_MIXER_BASS]));
574a52604cfSOrion Hodson 		bzero(&codec->mix[SOUND_MIXER_TREBLE],
575a52604cfSOrion Hodson 		      sizeof(codec->mix[SOUND_MIXER_TREBLE]));
576a52604cfSOrion Hodson 	}
577108082c4SOrion Hodson }
578108082c4SOrion Hodson 
57919921b23SOrion Hodson static const char*
58019921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
58119921b23SOrion Hodson {
58219921b23SOrion Hodson 	if (cname == NULL) {
58319921b23SOrion Hodson 		sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
58419921b23SOrion Hodson 		return buf;
58519921b23SOrion Hodson 	}
58619921b23SOrion Hodson 
58719921b23SOrion Hodson 	if (vname == NULL) vname = "Unknown";
58819921b23SOrion Hodson 
58919921b23SOrion Hodson 	if (bootverbose) {
59019921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
59119921b23SOrion Hodson 	} else {
59219921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec", vname, cname);
59319921b23SOrion Hodson 	}
59419921b23SOrion Hodson 	return buf;
59519921b23SOrion Hodson }
59619921b23SOrion Hodson 
597987e5972SCameron Grant static unsigned
59839004e69SCameron Grant ac97_initmixer(struct ac97_info *codec)
599987e5972SCameron Grant {
600f9eb1409SOrion Hodson 	ac97_patch codec_patch;
60119921b23SOrion Hodson 	const char *cname, *vname;
60219921b23SOrion Hodson 	char desc[80];
60319921b23SOrion Hodson 	u_int8_t model, step;
60486336196SAlexander Leidinger 	unsigned i, j, k, bit, old;
605987e5972SCameron Grant 	u_int32_t id;
60686336196SAlexander Leidinger 	int reg;
607987e5972SCameron Grant 
60866ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
6090f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
610cd2c103aSCameron Grant 	if (codec->count == 0) {
61104553e63SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
61266ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
61304553e63SCameron Grant 		return ENODEV;
61404553e63SCameron Grant 	}
6159ec437a3SCameron Grant 
616f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
617c6d4b83aSOrion Hodson 	ac97_reset(codec);
618f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
619987e5972SCameron Grant 
620f9eb1409SOrion Hodson 	i = ac97_rdcd(codec, AC97_REG_RESET);
62186336196SAlexander Leidinger 	j = ac97_rdcd(codec, AC97_REG_RESET);
62269f6d261SAriff Abdullah 	k = 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 	 */
62869f6d261SAriff Abdullah 	if (i != j || j != k) {
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;
643fd7390d6SAriff Abdullah 	codec->subvendor = (u_int32_t)pci_get_subdevice(codec->dev) << 16;
644fd7390d6SAriff Abdullah 	codec->subvendor |= (u_int32_t)pci_get_subvendor(codec->dev) &
645fd7390d6SAriff Abdullah 	    0x0000ffff;
646cd2c103aSCameron Grant 	codec->noext = 0;
647f9eb1409SOrion Hodson 	codec_patch = NULL;
64819921b23SOrion Hodson 
64919921b23SOrion Hodson 	cname = NULL;
65019921b23SOrion Hodson 	model = step = 0;
651cd2c103aSCameron Grant 	for (i = 0; ac97codecid[i].id; i++) {
65219921b23SOrion Hodson 		u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
65319921b23SOrion Hodson 		if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
654cd2c103aSCameron Grant 			codec->noext = ac97codecid[i].noext;
655f9eb1409SOrion Hodson 			codec_patch = ac97codecid[i].patch;
65619921b23SOrion Hodson 			cname = ac97codecid[i].name;
65719921b23SOrion Hodson 			model = (id & modelmask) & 0xff;
65819921b23SOrion Hodson 			step = (id & ~modelmask) & 0xff;
65919921b23SOrion Hodson 			break;
66019921b23SOrion Hodson 		}
66119921b23SOrion Hodson 	}
66219921b23SOrion Hodson 
66319921b23SOrion Hodson 	vname = NULL;
66419921b23SOrion Hodson 	for (i = 0; ac97vendorid[i].id; i++) {
66519921b23SOrion Hodson 		if (ac97vendorid[i].id == (id & 0xffffff00)) {
66619921b23SOrion Hodson 			vname = ac97vendorid[i].name;
66719921b23SOrion Hodson 			break;
668cd2c103aSCameron Grant 		}
669cd2c103aSCameron Grant 	}
6706b4b88f7SCameron Grant 
671cd2c103aSCameron Grant 	codec->extcaps = 0;
672cd2c103aSCameron Grant 	codec->extid = 0;
673cd2c103aSCameron Grant 	codec->extstat = 0;
6746a6ee5bbSCameron Grant 	if (!codec->noext) {
675f9eb1409SOrion Hodson 		i = ac97_rdcd(codec, AC97_REGEXT_ID);
6766a6ee5bbSCameron Grant 		if (i != 0xffff) {
67739004e69SCameron Grant 			codec->extcaps = i & 0x3fff;
67839004e69SCameron Grant 			codec->extid =  (i & 0xc000) >> 14;
679f9eb1409SOrion Hodson 			codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
6806b4b88f7SCameron Grant 		}
6816a6ee5bbSCameron Grant 	}
682987e5972SCameron Grant 
68369f6d261SAriff Abdullah 	for (i = 0; i < AC97_MIXER_SIZE; i++) {
684108082c4SOrion Hodson 		codec->mix[i] = ac97mixtable_default[i];
685108082c4SOrion Hodson 	}
686108082c4SOrion Hodson 	ac97_fix_auxout(codec);
687a52604cfSOrion Hodson 	ac97_fix_tone(codec);
688f9eb1409SOrion Hodson 	if (codec_patch)
689f9eb1409SOrion Hodson 		codec_patch(codec);
690108082c4SOrion Hodson 
69169f6d261SAriff Abdullah 	for (i = 0; i < AC97_MIXER_SIZE; i++) {
69233c878f0SCameron Grant 		k = codec->noext? codec->mix[i].enable : 1;
69386336196SAlexander Leidinger 		reg = codec->mix[i].reg;
69486336196SAlexander Leidinger 		if (reg < 0)
69586336196SAlexander Leidinger 			reg = -reg;
69686336196SAlexander Leidinger 		if (k && reg) {
69786336196SAlexander Leidinger 			j = old = ac97_rdcd(codec, reg);
69886336196SAlexander Leidinger 			/*
69986336196SAlexander Leidinger 			 * Test for mute bit (except for AC97_MIX_TONE,
70086336196SAlexander Leidinger 			 * where we simply assume it as available).
70186336196SAlexander Leidinger 			 */
70286336196SAlexander Leidinger 			if (codec->mix[i].mute) {
70386336196SAlexander Leidinger 				ac97_wrcd(codec, reg, j | 0x8000);
70486336196SAlexander Leidinger 				j = ac97_rdcd(codec, reg);
70586336196SAlexander Leidinger 			} else
706fe435202SAlexander Leidinger 				j |= 0x8000;
70786336196SAlexander Leidinger 			if ((j & 0x8000)) {
70886336196SAlexander Leidinger 				/*
70986336196SAlexander Leidinger 				 * Test whether the control width should be
71086336196SAlexander Leidinger 				 * 4, 5 or 6 bit. For 5bit register, we should
71186336196SAlexander Leidinger 				 * test it whether it's really 5 or 6bit. Leave
71286336196SAlexander Leidinger 				 * 4bit register alone, because sometimes an
71386336196SAlexander Leidinger 				 * attempt to write past 4th bit may cause
71486336196SAlexander Leidinger 				 * incorrect result especially for AC97_MIX_BEEP
71586336196SAlexander Leidinger 				 * (ac97 2.3).
71686336196SAlexander Leidinger 				 */
71786336196SAlexander Leidinger 				bit = codec->mix[i].bits;
71886336196SAlexander Leidinger 				if (bit == 5)
71986336196SAlexander Leidinger 					bit++;
72086336196SAlexander Leidinger 				j = ((1 << bit) - 1) << codec->mix[i].ofs;
72186336196SAlexander Leidinger 				ac97_wrcd(codec, reg,
72286336196SAlexander Leidinger 					j | (codec->mix[i].mute ? 0x8000 : 0));
72386336196SAlexander Leidinger 				k = ac97_rdcd(codec, reg) & j;
72486336196SAlexander Leidinger 				k >>= codec->mix[i].ofs;
72586336196SAlexander Leidinger 				if (reg == AC97_MIX_TONE &&
72686336196SAlexander Leidinger 							((k & 0x0001) == 0x0000))
727fe435202SAlexander Leidinger 					k >>= 1;
72886336196SAlexander Leidinger 				for (j = 0; k >> j; j++)
72986336196SAlexander Leidinger 					;
730fe435202SAlexander Leidinger 				if (j != 0) {
731fe435202SAlexander Leidinger #if 0
73286336196SAlexander Leidinger 					device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n",
73386336196SAlexander Leidinger 						i, k, bit, codec->mix[i].bits, j);
734fe435202SAlexander Leidinger #endif
73586336196SAlexander Leidinger 					codec->mix[i].enable = 1;
73686336196SAlexander Leidinger 					codec->mix[i].bits = j;
737eaf70083SAriff Abdullah 				} else if (reg == AC97_MIX_BEEP) {
738eaf70083SAriff Abdullah 					/*
739eaf70083SAriff Abdullah 					 * Few codec such as CX20468-21 does
740eaf70083SAriff Abdullah 					 * have this control register, although
741eaf70083SAriff Abdullah 					 * the only usable part is the mute bit.
742eaf70083SAriff Abdullah 					 */
743eaf70083SAriff Abdullah 					codec->mix[i].enable = 1;
744fe435202SAlexander Leidinger 				} else
745fe435202SAlexander Leidinger 					codec->mix[i].enable = 0;
746fe435202SAlexander Leidinger 			} else
747fe435202SAlexander Leidinger 				codec->mix[i].enable = 0;
74886336196SAlexander Leidinger 			ac97_wrcd(codec, reg, old);
749fe435202SAlexander Leidinger 		}
750fe435202SAlexander Leidinger #if 0
751fe435202SAlexander Leidinger 		printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits);
752fe435202SAlexander Leidinger #endif
753341f16ccSCameron Grant 	}
754987e5972SCameron Grant 
75519921b23SOrion Hodson 	device_printf(codec->dev, "<%s>\n",
75619921b23SOrion Hodson 		      ac97_hw_desc(codec->id, vname, cname, desc));
757a825c6e5SOrion Hodson 
758987e5972SCameron Grant 	if (bootverbose) {
75986336196SAlexander Leidinger 		if (codec->flags & AC97_F_RDCD_BUG)
76086336196SAlexander Leidinger 			device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n");
76119921b23SOrion Hodson 		device_printf(codec->dev, "Codec features ");
76239004e69SCameron Grant 		for (i = j = 0; i < 10; i++)
76339004e69SCameron Grant 			if (codec->caps & (1 << i))
76439004e69SCameron Grant 				printf("%s%s", j++? ", " : "", ac97feature[i]);
76539004e69SCameron Grant 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
766987e5972SCameron Grant 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
76739004e69SCameron Grant 
76839004e69SCameron Grant 		if (codec->extcaps != 0 || codec->extid) {
76919921b23SOrion Hodson 			device_printf(codec->dev, "%s codec",
77019921b23SOrion Hodson 				      codec->extid? "Secondary" : "Primary");
77139004e69SCameron Grant 			if (codec->extcaps)
77239004e69SCameron Grant 				printf(" extended features ");
77339004e69SCameron Grant 			for (i = j = 0; i < 14; i++)
77439004e69SCameron Grant 				if (codec->extcaps & (1 << i))
77539004e69SCameron Grant 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
77639004e69SCameron Grant 			printf("\n");
77739004e69SCameron Grant 		}
778987e5972SCameron Grant 	}
779987e5972SCameron Grant 
780fe435202SAlexander Leidinger 	i = 0;
781fe435202SAlexander Leidinger 	while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) {
782fe435202SAlexander Leidinger 		if (++i == 100) {
78303a00905SCameron Grant 			device_printf(codec->dev, "ac97 codec reports dac not ready\n");
784fe435202SAlexander Leidinger 			break;
785fe435202SAlexander Leidinger 		}
786fe435202SAlexander Leidinger 		DELAY(1000);
787fe435202SAlexander Leidinger 	}
788fe435202SAlexander Leidinger 	if (bootverbose)
789fe435202SAlexander Leidinger 		device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i);
79066ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
791987e5972SCameron Grant 	return 0;
792987e5972SCameron Grant }
793987e5972SCameron Grant 
7949ec437a3SCameron Grant static unsigned
7959ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec)
7969ec437a3SCameron Grant {
79766ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
7980f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
7999ec437a3SCameron Grant 	if (codec->count == 0) {
8009ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
80166ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
8029ec437a3SCameron Grant 		return ENODEV;
8039ec437a3SCameron Grant 	}
8049ec437a3SCameron Grant 
805f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
806c6d4b83aSOrion Hodson 	ac97_reset(codec);
807f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
8089ec437a3SCameron Grant 
8099ec437a3SCameron Grant 	if (!codec->noext) {
810f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
811f9eb1409SOrion Hodson 		if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
812b60e55dbSGuido van Rooij 		    != codec->extstat)
8139ec437a3SCameron Grant 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
814b60e55dbSGuido van Rooij 				      codec->extstat,
815f9eb1409SOrion Hodson 				      ac97_rdcd(codec, AC97_REGEXT_STAT) &
816b60e55dbSGuido van Rooij 				      AC97_EXTCAPS);
8179ec437a3SCameron Grant 	}
8189ec437a3SCameron Grant 
819f9eb1409SOrion Hodson 	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
8209ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
82166ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
8229ec437a3SCameron Grant 	return 0;
8239ec437a3SCameron Grant }
8249ec437a3SCameron Grant 
825987e5972SCameron Grant struct ac97_info *
8260f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
827987e5972SCameron Grant {
828987e5972SCameron Grant 	struct ac97_info *codec;
82990da2b28SAriff Abdullah 	int i;
830987e5972SCameron Grant 
831082f6383SAriff Abdullah 	codec = malloc(sizeof(*codec), M_AC97, M_WAITOK | M_ZERO);
83269f6d261SAriff Abdullah 	snprintf(codec->name, sizeof(codec->name), "%s:ac97",
83369f6d261SAriff Abdullah 	    device_get_nameunit(dev));
834489c22ebSJohn Baldwin 	codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
8357699548fSAriff Abdullah 	codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO);
8360f55ac6cSCameron Grant 	codec->dev = dev;
8370f55ac6cSCameron Grant 	codec->devinfo = devinfo;
83879bb7d52SCameron Grant 	codec->flags = 0;
83990da2b28SAriff Abdullah 
8407699548fSAriff Abdullah 	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
84190da2b28SAriff Abdullah 	    "eapdinv", &i) == 0 && i != 0)
8427699548fSAriff Abdullah 		codec->flags |= AC97_F_EAPD_INV;
84390da2b28SAriff Abdullah 
84490da2b28SAriff Abdullah 	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
84590da2b28SAriff Abdullah 	    "softpcmvol", &i) == 0 && i != 0)
84690da2b28SAriff Abdullah 		pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL);
84790da2b28SAriff Abdullah 
848987e5972SCameron Grant 	return codec;
849987e5972SCameron Grant }
850987e5972SCameron Grant 
85133dbf14aSCameron Grant void
85233dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec)
85333dbf14aSCameron Grant {
85466ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
8550f55ac6cSCameron Grant 	if (codec->methods != NULL)
8560f55ac6cSCameron Grant 		kobj_delete(codec->methods, M_AC97);
85766ef8af5SCameron Grant 	snd_mtxfree(codec->lock);
8580f55ac6cSCameron Grant 	free(codec, M_AC97);
85933dbf14aSCameron Grant }
86033dbf14aSCameron Grant 
86179bb7d52SCameron Grant void
86279bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val)
86379bb7d52SCameron Grant {
86479bb7d52SCameron Grant 	codec->flags = val;
86579bb7d52SCameron Grant }
86679bb7d52SCameron Grant 
86779bb7d52SCameron Grant u_int32_t
86879bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec)
86979bb7d52SCameron Grant {
87079bb7d52SCameron Grant 	return codec->flags;
87179bb7d52SCameron Grant }
87279bb7d52SCameron Grant 
8730f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
8740f55ac6cSCameron Grant 
875a580b31aSAriff Abdullah static int
876a580b31aSAriff Abdullah sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS)
877a580b31aSAriff Abdullah {
878a580b31aSAriff Abdullah 	struct ac97_info *codec;
879a580b31aSAriff Abdullah 	int ea, inv, err = 0;
880a580b31aSAriff Abdullah 	u_int16_t val;
881a580b31aSAriff Abdullah 
882a580b31aSAriff Abdullah 	codec = oidp->oid_arg1;
883a580b31aSAriff Abdullah 	if (codec == NULL || codec->id == 0 || codec->lock == NULL)
884a580b31aSAriff Abdullah 		return EINVAL;
885a580b31aSAriff Abdullah 	snd_mtxlock(codec->lock);
886a580b31aSAriff Abdullah 	val = ac97_rdcd(codec, AC97_REG_POWER);
887a580b31aSAriff Abdullah 	inv = (codec->flags & AC97_F_EAPD_INV) ? 0 : 1;
888a580b31aSAriff Abdullah 	ea = (val >> 15) ^ inv;
889a580b31aSAriff Abdullah 	snd_mtxunlock(codec->lock);
890041b706bSDavid Malone 	err = sysctl_handle_int(oidp, &ea, 0, req);
891a580b31aSAriff Abdullah 	if (err == 0 && req->newptr != NULL) {
892a580b31aSAriff Abdullah 		if (ea != 0 && ea != 1)
893a580b31aSAriff Abdullah 			return EINVAL;
894a580b31aSAriff Abdullah 		if (ea != ((val >> 15) ^ inv)) {
895a580b31aSAriff Abdullah 			snd_mtxlock(codec->lock);
896a580b31aSAriff Abdullah 			ac97_wrcd(codec, AC97_REG_POWER, val ^ 0x8000);
897a580b31aSAriff Abdullah 			snd_mtxunlock(codec->lock);
898a580b31aSAriff Abdullah 		}
899a580b31aSAriff Abdullah 	}
900a580b31aSAriff Abdullah 	return err;
901a580b31aSAriff Abdullah }
902a580b31aSAriff Abdullah 
903a580b31aSAriff Abdullah static void
904a580b31aSAriff Abdullah ac97_init_sysctl(struct ac97_info *codec)
905a580b31aSAriff Abdullah {
906a580b31aSAriff Abdullah 	u_int16_t orig, val;
907a580b31aSAriff Abdullah 
908a580b31aSAriff Abdullah 	if (codec == NULL || codec->dev == NULL)
909a580b31aSAriff Abdullah 		return;
910a580b31aSAriff Abdullah 	snd_mtxlock(codec->lock);
911a580b31aSAriff Abdullah 	orig = ac97_rdcd(codec, AC97_REG_POWER);
912a580b31aSAriff Abdullah 	ac97_wrcd(codec, AC97_REG_POWER, orig ^ 0x8000);
913a580b31aSAriff Abdullah 	val = ac97_rdcd(codec, AC97_REG_POWER);
914a580b31aSAriff Abdullah 	ac97_wrcd(codec, AC97_REG_POWER, orig);
915a580b31aSAriff Abdullah 	snd_mtxunlock(codec->lock);
916a580b31aSAriff Abdullah 	if ((val & 0x8000) == (orig & 0x8000))
917a580b31aSAriff Abdullah 		return;
918a580b31aSAriff Abdullah 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(codec->dev),
919a580b31aSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(codec->dev)),
920a580b31aSAriff Abdullah             OID_AUTO, "eapd", CTLTYPE_INT | CTLFLAG_RW,
921a580b31aSAriff Abdullah 	    codec, sizeof(codec), sysctl_hw_snd_ac97_eapd,
922a580b31aSAriff Abdullah 	    "I", "AC97 External Amplifier");
923a580b31aSAriff Abdullah }
924a580b31aSAriff Abdullah 
925987e5972SCameron Grant static int
92666ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m)
927987e5972SCameron Grant {
928987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
929341f16ccSCameron Grant 	u_int32_t i, mask;
930341f16ccSCameron Grant 
93139004e69SCameron Grant 	if (codec == NULL)
93239004e69SCameron Grant 		return -1;
933341f16ccSCameron Grant 
934e620d959SCameron Grant 	if (ac97_initmixer(codec))
935e620d959SCameron Grant 		return -1;
936341f16ccSCameron Grant 
9377699548fSAriff Abdullah 	switch (codec->id) {
9387699548fSAriff Abdullah 	case 0x41445374:	/* AD1981B */
93955431491SAriff Abdullah 		switch (codec->subvendor) {
94055431491SAriff Abdullah 		case 0x02d91014:
941fd7390d6SAriff Abdullah 			/*
942fd7390d6SAriff Abdullah 			 * IBM Thinkcentre:
94355431491SAriff Abdullah 			 *
94455431491SAriff Abdullah 			 * Tie "ogain" and "phout" to "vol" since its
945fd7390d6SAriff Abdullah 			 * master volume is basically useless and can't
946fd7390d6SAriff Abdullah 			 * control anything.
947fd7390d6SAriff Abdullah 			 */
9487699548fSAriff Abdullah 			mask = 0;
9497699548fSAriff Abdullah 			if (codec->mix[SOUND_MIXER_OGAIN].enable)
9507699548fSAriff Abdullah 				mask |= SOUND_MASK_OGAIN;
9517699548fSAriff Abdullah 			if (codec->mix[SOUND_MIXER_PHONEOUT].enable)
9527699548fSAriff Abdullah 				mask |= SOUND_MASK_PHONEOUT;
9537699548fSAriff Abdullah 			if (codec->mix[SOUND_MIXER_VOLUME].enable)
954a580b31aSAriff Abdullah 				mix_setparentchild(m, SOUND_MIXER_VOLUME,
955a580b31aSAriff Abdullah 				    mask);
9567699548fSAriff Abdullah 			else {
957a580b31aSAriff Abdullah 				mix_setparentchild(m, SOUND_MIXER_VOLUME,
958a580b31aSAriff Abdullah 				    mask);
959a580b31aSAriff Abdullah 				mix_setrealdev(m, SOUND_MIXER_VOLUME,
960a580b31aSAriff Abdullah 				    SOUND_MIXER_NONE);
9617699548fSAriff Abdullah 			}
96255431491SAriff Abdullah 			break;
96355431491SAriff Abdullah 		case 0x099c103c:
96455431491SAriff Abdullah 			/*
96555431491SAriff Abdullah 			 * HP nx6110:
96655431491SAriff Abdullah 			 *
96755431491SAriff Abdullah 			 * By default, "vol" is controlling internal speakers
96855431491SAriff Abdullah 			 * (not a master volume!) and "ogain" is controlling
96955431491SAriff Abdullah 			 * headphone. Enable dummy "phout" so it can be
97055431491SAriff Abdullah 			 * remapped to internal speakers and virtualize
97155431491SAriff Abdullah 			 * "vol" to control both.
97255431491SAriff Abdullah 			 */
97355431491SAriff Abdullah 			codec->mix[SOUND_MIXER_OGAIN].enable = 1;
97455431491SAriff Abdullah 			codec->mix[SOUND_MIXER_PHONEOUT].enable = 1;
97555431491SAriff Abdullah 			mix_setrealdev(m, SOUND_MIXER_PHONEOUT,
97655431491SAriff Abdullah 			    SOUND_MIXER_VOLUME);
97755431491SAriff Abdullah 			mix_setrealdev(m, SOUND_MIXER_VOLUME,
97855431491SAriff Abdullah 			    SOUND_MIXER_NONE);
97955431491SAriff Abdullah 			mix_setparentchild(m, SOUND_MIXER_VOLUME,
98055431491SAriff Abdullah 			    SOUND_MASK_OGAIN | SOUND_MASK_PHONEOUT);
98155431491SAriff Abdullah 			break;
98255431491SAriff Abdullah 		default:
98355431491SAriff Abdullah 			break;
984a580b31aSAriff Abdullah 		}
9857699548fSAriff Abdullah 		break;
9867699548fSAriff Abdullah 	case 0x434d4941:	/* CMI9738 */
9877699548fSAriff Abdullah 	case 0x434d4961:	/* CMI9739 */
9887699548fSAriff Abdullah 	case 0x434d4978:	/* CMI9761 */
9897699548fSAriff Abdullah 	case 0x434d4982:	/* CMI9761 */
9907699548fSAriff Abdullah 	case 0x434d4983:	/* CMI9761 */
9917699548fSAriff Abdullah 		bzero(&codec->mix[SOUND_MIXER_PCM],
9927699548fSAriff Abdullah 		    sizeof(codec->mix[SOUND_MIXER_PCM]));
993e510f521SAriff Abdullah 		pcm_setflags(codec->dev, pcm_getflags(codec->dev) |
994e510f521SAriff Abdullah 		    SD_F_SOFTPCMVOL);
9957699548fSAriff Abdullah 		/* XXX How about master volume ? */
9967699548fSAriff Abdullah 		break;
9977699548fSAriff Abdullah 	default:
9987699548fSAriff Abdullah 		break;
9997699548fSAriff Abdullah 	}
10007699548fSAriff Abdullah 
100190da2b28SAriff Abdullah 	if (pcm_getflags(codec->dev) & SD_F_SOFTPCMVOL)
100290da2b28SAriff Abdullah 		ac97_wrcd(codec, AC97_MIX_PCM, 0);
10037699548fSAriff Abdullah #if 0
10047699548fSAriff Abdullah 	/* XXX For the sake of debugging purposes */
10057699548fSAriff Abdullah 	mix_setparentchild(m, SOUND_MIXER_VOLUME,
10067699548fSAriff Abdullah 	    SOUND_MASK_PCM | SOUND_MASK_CD);
10077699548fSAriff Abdullah 	mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
10087699548fSAriff Abdullah 	ac97_wrcd(codec, AC97_MIX_MASTER, 0);
10097699548fSAriff Abdullah #endif
10107699548fSAriff Abdullah 
1011341f16ccSCameron Grant 	mask = 0;
101269f6d261SAriff Abdullah 	for (i = 0; i < AC97_MIXER_SIZE; i++)
1013341f16ccSCameron Grant 		mask |= codec->mix[i].enable? 1 << i : 0;
1014341f16ccSCameron Grant 	mix_setdevs(m, mask);
1015341f16ccSCameron Grant 
1016341f16ccSCameron Grant 	mask = 0;
101769f6d261SAriff Abdullah 	for (i = 0; i < AC97_MIXER_SIZE; i++)
1018341f16ccSCameron Grant 		mask |= codec->mix[i].recidx? 1 << i : 0;
1019341f16ccSCameron Grant 	mix_setrecdevs(m, mask);
1020a580b31aSAriff Abdullah 
1021a580b31aSAriff Abdullah 	ac97_init_sysctl(codec);
1022a580b31aSAriff Abdullah 
1023987e5972SCameron Grant 	return 0;
1024987e5972SCameron Grant }
1025987e5972SCameron Grant 
1026987e5972SCameron Grant static int
102766ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m)
102833dbf14aSCameron Grant {
102933dbf14aSCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
1030341f16ccSCameron Grant 
103133dbf14aSCameron Grant 	if (codec == NULL)
103233dbf14aSCameron Grant 		return -1;
103333dbf14aSCameron Grant 	/*
103433dbf14aSCameron Grant 	if (ac97_uninitmixer(codec))
103533dbf14aSCameron Grant 		return -1;
103633dbf14aSCameron Grant 	*/
103733dbf14aSCameron Grant 	ac97_destroy(codec);
103833dbf14aSCameron Grant 	return 0;
103933dbf14aSCameron Grant }
104033dbf14aSCameron Grant 
104133dbf14aSCameron Grant static int
104266ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m)
10439ec437a3SCameron Grant {
10449ec437a3SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
10459ec437a3SCameron Grant 
10469ec437a3SCameron Grant 	if (codec == NULL)
10479ec437a3SCameron Grant 		return -1;
10489ec437a3SCameron Grant 	return ac97_reinitmixer(codec);
10499ec437a3SCameron Grant }
10509ec437a3SCameron Grant 
10519ec437a3SCameron Grant static int
105266ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
1053987e5972SCameron Grant {
1054987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
1055341f16ccSCameron Grant 
105669f6d261SAriff Abdullah 	if (codec == NULL || dev >= AC97_MIXER_SIZE)
105739004e69SCameron Grant 		return -1;
1058987e5972SCameron Grant 	return ac97_setmixer(codec, dev, left, right);
1059987e5972SCameron Grant }
1060987e5972SCameron Grant 
106190da2b28SAriff Abdullah static u_int32_t
106266ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
1063987e5972SCameron Grant {
1064987e5972SCameron Grant 	int i;
1065987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
1066341f16ccSCameron Grant 
106739004e69SCameron Grant 	if (codec == NULL)
106839004e69SCameron Grant 		return -1;
106969f6d261SAriff Abdullah 	for (i = 0; i < AC97_MIXER_SIZE; i++)
107039004e69SCameron Grant 		if ((src & (1 << i)) != 0)
107139004e69SCameron Grant 			break;
107290da2b28SAriff Abdullah 	return (ac97_setrecsrc(codec, i) == 0)? 1U << i : 0xffffffffU;
1073987e5972SCameron Grant }
1074987e5972SCameron Grant 
10750f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = {
10760f55ac6cSCameron Grant     	KOBJMETHOD(mixer_init,		ac97mix_init),
10770f55ac6cSCameron Grant     	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
10780f55ac6cSCameron Grant     	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
10790f55ac6cSCameron Grant     	KOBJMETHOD(mixer_set,		ac97mix_set),
10800f55ac6cSCameron Grant     	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
108190da2b28SAriff Abdullah 	KOBJMETHOD_END
1082987e5972SCameron Grant };
10830f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer);
10840f55ac6cSCameron Grant 
10850f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
10860f55ac6cSCameron Grant 
10870f55ac6cSCameron Grant kobj_class_t
10880f55ac6cSCameron Grant ac97_getmixerclass(void)
10890f55ac6cSCameron Grant {
10900f55ac6cSCameron Grant 	return &ac97mixer_class;
10910f55ac6cSCameron Grant }
1092