xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision 7029da5c36f2d3cf6bb6c81bf551229f416399e8)
1098ca2bdSWarner Losh /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
43f225978SCameron Grant  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
5987e5972SCameron Grant  * All rights reserved.
6987e5972SCameron Grant  *
7987e5972SCameron Grant  * Redistribution and use in source and binary forms, with or without
8987e5972SCameron Grant  * modification, are permitted provided that the following conditions
9987e5972SCameron Grant  * are met:
10987e5972SCameron Grant  * 1. Redistributions of source code must retain the above copyright
11987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer.
12987e5972SCameron Grant  * 2. Redistributions in binary form must reproduce the above copyright
13987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer in the
14987e5972SCameron Grant  *    documentation and/or other materials provided with the distribution.
15987e5972SCameron Grant  *
16987e5972SCameron Grant  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17987e5972SCameron Grant  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18987e5972SCameron Grant  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19987e5972SCameron Grant  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20987e5972SCameron Grant  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21987e5972SCameron Grant  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22987e5972SCameron Grant  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23987e5972SCameron Grant  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24987e5972SCameron Grant  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25987e5972SCameron Grant  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26987e5972SCameron Grant  * SUCH DAMAGE.
27987e5972SCameron Grant  */
28987e5972SCameron Grant 
2990da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS
3090da2b28SAriff Abdullah #include "opt_snd.h"
3190da2b28SAriff Abdullah #endif
3290da2b28SAriff Abdullah 
33ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
34ef9308b1SCameron Grant #include <dev/sound/pcm/ac97.h>
35f9eb1409SOrion Hodson #include <dev/sound/pcm/ac97_patch.h>
36987e5972SCameron Grant 
37a580b31aSAriff Abdullah #include <dev/pci/pcivar.h>
38a580b31aSAriff Abdullah 
390f55ac6cSCameron Grant #include "mixer_if.h"
40987e5972SCameron Grant 
4167b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$");
4267b1dce3SCameron Grant 
43d745c852SEd Schouten static MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
44987e5972SCameron Grant 
4579bb7d52SCameron Grant struct ac97mixtable_entry {
4683234531SAriff Abdullah 	int reg;		/* register index		*/
4796524a52SOrion Hodson 				/* reg < 0 if inverted polarity	*/
4896524a52SOrion Hodson 	unsigned bits:4;	/* width of control field	*/
4996524a52SOrion Hodson 	unsigned ofs:4;		/* offset (only if stereo=0)	*/
5096524a52SOrion Hodson 	unsigned stereo:1;	/* set for stereo controls	*/
5196524a52SOrion Hodson 	unsigned mute:1;	/* bit15 is MUTE		*/
5296524a52SOrion Hodson 	unsigned recidx:4;	/* index in rec mux		*/
5396524a52SOrion Hodson 	unsigned mask:1;	/* use only masked bits		*/
5496524a52SOrion Hodson 	unsigned enable:1;	/* entry is enabled		*/
5579bb7d52SCameron Grant };
5679bb7d52SCameron Grant 
5769f6d261SAriff Abdullah #define AC97_MIXER_SIZE		SOUND_MIXER_NRDEVICES
5869f6d261SAriff Abdullah 
5966ef8af5SCameron Grant struct ac97_info {
6066ef8af5SCameron Grant 	kobj_t methods;
6166ef8af5SCameron Grant 	device_t dev;
6266ef8af5SCameron Grant 	void *devinfo;
6319921b23SOrion Hodson 	u_int32_t id;
64fd7390d6SAriff Abdullah 	u_int32_t subvendor;
6566ef8af5SCameron Grant 	unsigned count, caps, se, extcaps, extid, extstat, noext:1;
6679bb7d52SCameron Grant 	u_int32_t flags;
6769f6d261SAriff Abdullah 	struct ac97mixtable_entry mix[AC97_MIXER_SIZE];
6869f6d261SAriff Abdullah 	char name[16];
6900acb133SCameron Grant 	struct mtx *lock;
7066ef8af5SCameron Grant };
7166ef8af5SCameron Grant 
7219921b23SOrion Hodson struct ac97_vendorid {
7319921b23SOrion Hodson 	u_int32_t   id;
7419921b23SOrion Hodson 	const char *name;
7519921b23SOrion Hodson };
7619921b23SOrion Hodson 
77987e5972SCameron Grant struct ac97_codecid {
7819921b23SOrion Hodson 	u_int32_t  id;
7919921b23SOrion Hodson 	u_int8_t   stepmask;
8019921b23SOrion Hodson 	u_int8_t   noext:1;
81987e5972SCameron Grant 	char 	  *name;
82f9eb1409SOrion Hodson 	ac97_patch patch;
83987e5972SCameron Grant };
84987e5972SCameron Grant 
8569f6d261SAriff Abdullah static const struct ac97mixtable_entry ac97mixtable_default[AC97_MIXER_SIZE] = {
8696524a52SOrion Hodson     /*	[offset]			reg	     bits of st mu re mk en */
87341f16ccSCameron Grant 	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0, 1 },
88a52604cfSOrion Hodson 	[SOUND_MIXER_OGAIN]	= { AC97_MIX_AUXOUT, 	5, 0, 1, 1, 0, 0, 0 },
89341f16ccSCameron Grant 	[SOUND_MIXER_PHONEOUT]	= { AC97_MIX_MONO, 	5, 0, 0, 1, 7, 0, 0 },
90341f16ccSCameron Grant 	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1, 0 },
91341f16ccSCameron Grant 	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1, 0 },
92341f16ccSCameron Grant 	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0, 1 },
93341f16ccSCameron Grant 	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0, 0 },
94341f16ccSCameron Grant 	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0, 1 },
95341f16ccSCameron Grant 	[SOUND_MIXER_PHONEIN]	= { AC97_MIX_PHONE, 	5, 0, 0, 1, 8, 0, 0 },
9696524a52SOrion Hodson 	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 1, 1 },
9796524a52SOrion Hodson 	/* use igain for the mic 20dB boost */
9896524a52SOrion Hodson 	[SOUND_MIXER_IGAIN] 	= { -AC97_MIX_MIC, 	1, 6, 0, 0, 0, 1, 1 },
99341f16ccSCameron Grant 	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
100341f16ccSCameron Grant 	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
101341f16ccSCameron Grant 	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
102341f16ccSCameron Grant 	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
103987e5972SCameron Grant };
104987e5972SCameron Grant 
10519921b23SOrion Hodson static const struct ac97_vendorid ac97vendorid[] = {
10619921b23SOrion Hodson 	{ 0x41445300, "Analog Devices" },
10719921b23SOrion Hodson 	{ 0x414b4d00, "Asahi Kasei" },
10819921b23SOrion Hodson 	{ 0x414c4300, "Realtek" },
10919921b23SOrion Hodson 	{ 0x414c4700, "Avance Logic" },
11019921b23SOrion Hodson 	{ 0x43525900, "Cirrus Logic" },
11119921b23SOrion Hodson 	{ 0x434d4900, "C-Media Electronics" },
11219921b23SOrion Hodson 	{ 0x43585400, "Conexant" },
1133c6b655dSMathew Kanner 	{ 0x44543000, "Diamond Technology" },
11461a0da1dSOrion Hodson 	{ 0x454d4300, "eMicro" },
11519921b23SOrion Hodson 	{ 0x45838300, "ESS Technology" },
1163c6b655dSMathew Kanner 	{ 0x48525300, "Intersil" },
11719921b23SOrion Hodson 	{ 0x49434500, "ICEnsemble" },
1183c6b655dSMathew Kanner 	{ 0x49544500, "ITE, Inc." },
11919921b23SOrion Hodson 	{ 0x4e534300, "National Semiconductor" },
12019921b23SOrion Hodson 	{ 0x50534300, "Philips Semiconductor" },
12119921b23SOrion Hodson 	{ 0x83847600, "SigmaTel" },
1223c6b655dSMathew Kanner 	{ 0x53494c00, "Silicon Laboratories" },
12319921b23SOrion Hodson 	{ 0x54524100, "TriTech" },
1243c6b655dSMathew Kanner 	{ 0x54584e00, "Texas Instruments" },
12519921b23SOrion Hodson 	{ 0x56494100, "VIA Technologies" },
1263c6b655dSMathew Kanner 	{ 0x57454300, "Winbond" },
12719921b23SOrion Hodson 	{ 0x574d4c00, "Wolfson" },
12819921b23SOrion Hodson 	{ 0x594d4800, "Yamaha" },
12986336196SAlexander Leidinger 	/*
13086336196SAlexander Leidinger 	 * XXX This is a fluke, really! The real vendor
13186336196SAlexander Leidinger 	 * should be SigmaTel, not this! This should be
13286336196SAlexander Leidinger 	 * removed someday!
13386336196SAlexander Leidinger 	 */
1346f0182bdSOrion Hodson 	{ 0x01408300, "Creative" },
13519921b23SOrion Hodson 	{ 0x00000000, NULL }
13619921b23SOrion Hodson };
13719921b23SOrion Hodson 
138987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = {
13919921b23SOrion Hodson 	{ 0x41445303, 0x00, 0, "AD1819",	0 },
14019921b23SOrion Hodson 	{ 0x41445340, 0x00, 0, "AD1881",	0 },
14119921b23SOrion Hodson 	{ 0x41445348, 0x00, 0, "AD1881A",	0 },
14219921b23SOrion Hodson 	{ 0x41445360, 0x00, 0, "AD1885",	0 },
14319921b23SOrion Hodson 	{ 0x41445361, 0x00, 0, "AD1886", 	ad1886_patch },
144ba548c64SOrion Hodson 	{ 0x41445362, 0x00, 0, "AD1887", 	0 },
145ba548c64SOrion Hodson 	{ 0x41445363, 0x00, 0, "AD1886A", 	0 },
146c5cb8d60SScott Long 	{ 0x41445368, 0x00, 0, "AD1888", 	ad198x_patch },
147a52604cfSOrion Hodson 	{ 0x41445370, 0x00, 0, "AD1980",	ad198x_patch },
148ba548c64SOrion Hodson 	{ 0x41445372, 0x00, 0, "AD1981A",	0 },
1497699548fSAriff Abdullah 	{ 0x41445374, 0x00, 0, "AD1981B",	ad1981b_patch },
150a52604cfSOrion Hodson 	{ 0x41445375, 0x00, 0, "AD1985",	ad198x_patch },
151e1e05d5dSAriff Abdullah 	{ 0x41445378, 0x00, 0, "AD1986",	ad198x_patch },
15219921b23SOrion Hodson 	{ 0x414b4d00, 0x00, 1, "AK4540", 	0 },
15319921b23SOrion Hodson 	{ 0x414b4d01, 0x00, 1, "AK4542", 	0 },
15419921b23SOrion Hodson 	{ 0x414b4d02, 0x00, 1, "AK4543", 	0 },
1553c6b655dSMathew Kanner 	{ 0x414b4d06, 0x00, 0, "AK4544A",	0 },
1563c6b655dSMathew Kanner 	{ 0x454b4d07, 0x00, 0, "AK4545",	0 },
15719921b23SOrion Hodson 	{ 0x414c4320, 0x0f, 0, "ALC100",	0 },
1589963235aSOrion Hodson 	{ 0x414c4730, 0x0f, 0, "ALC101",	0 },
15919921b23SOrion Hodson 	{ 0x414c4710, 0x0f, 0, "ALC200", 	0 },
16019921b23SOrion Hodson 	{ 0x414c4740, 0x0f, 0, "ALC202", 	0 },
16119921b23SOrion Hodson 	{ 0x414c4720, 0x0f, 0, "ALC650", 	0 },
162b327ee51SAriff Abdullah 	{ 0x414c4752, 0x0f, 0, "ALC250",	0 },
163fd7390d6SAriff Abdullah 	{ 0x414c4760, 0x0f, 0, "ALC655",	alc655_patch },
164b7994e34SPyun YongHyeon 	{ 0x414c4770, 0x0f, 0, "ALC203",	0 },
16594ed763dSJun Kuriyama 	{ 0x414c4780, 0x0f, 0, "ALC658",	0 },
1663c6b655dSMathew Kanner 	{ 0x414c4790, 0x0f, 0, "ALC850",	0 },
16719921b23SOrion Hodson 	{ 0x43525900, 0x07, 0, "CS4297", 	0 },
16819921b23SOrion Hodson 	{ 0x43525910, 0x07, 0, "CS4297A", 	0 },
16919921b23SOrion Hodson 	{ 0x43525920, 0x07, 0, "CS4294/98",	0 },
170e5728f83SMIHIRA Sanpei Yoshiro 	{ 0x4352592d, 0x07, 0, "CS4294",	0 },
17119921b23SOrion Hodson 	{ 0x43525930, 0x07, 0, "CS4299",	0 },
17219921b23SOrion Hodson 	{ 0x43525940, 0x07, 0, "CS4201",	0 },
173b2a0f525SOrion Hodson 	{ 0x43525958, 0x07, 0, "CS4205",	0 },
17419921b23SOrion Hodson 	{ 0x43525960, 0x07, 0, "CS4291A",	0 },
175cb44f623SAlexander Leidinger 	{ 0x434d4961, 0x00, 0, "CMI9739",	cmi9739_patch },
17619921b23SOrion Hodson 	{ 0x434d4941, 0x00, 0, "CMI9738",	0 },
177cb44f623SAlexander Leidinger 	{ 0x434d4978, 0x00, 0, "CMI9761",	0 },
178cb44f623SAlexander Leidinger 	{ 0x434d4982, 0x00, 0, "CMI9761",	0 },
179cb44f623SAlexander Leidinger 	{ 0x434d4983, 0x00, 0, "CMI9761",	0 },
1803c6b655dSMathew Kanner 	{ 0x43585421, 0x00, 0, "HSD11246",	0 },
1813c6b655dSMathew Kanner 	{ 0x43585428, 0x07, 0, "CX20468",	0 },
18280138937SAriff Abdullah 	{ 0x43585430, 0x00, 0, "CX20468-21",	0 },
1833c6b655dSMathew Kanner 	{ 0x44543000, 0x00, 0, "DT0398",	0 },
18461a0da1dSOrion Hodson 	{ 0x454d4323, 0x00, 0, "EM28023",	0 },
18561a0da1dSOrion Hodson 	{ 0x454d4328, 0x00, 0, "EM28028",	0 },
18619921b23SOrion Hodson 	{ 0x45838308, 0x00, 0, "ES1988",	0 }, /* Formerly ES1921(?) */
1873c6b655dSMathew Kanner 	{ 0x48525300, 0x00, 0, "HMP9701",	0 },
18819921b23SOrion Hodson 	{ 0x49434501, 0x00, 0, "ICE1230",	0 },
18919921b23SOrion Hodson 	{ 0x49434511, 0x00, 0, "ICE1232",	0 },
19019921b23SOrion Hodson 	{ 0x49434514, 0x00, 0, "ICE1232A",	0 },
19149fd6905SOrion Hodson 	{ 0x49434551, 0x03, 0, "VT1616",	0 }, /* Via badged ICE */
1923c6b655dSMathew Kanner 	{ 0x49544520, 0x00, 0, "ITE2226E",	0 },
1933c6b655dSMathew Kanner 	{ 0x49544560, 0x07, 0, "ITE2646E",	0 }, /* XXX: patch needed */
19419921b23SOrion Hodson 	{ 0x4e534340, 0x00, 0, "LM4540",	0 }, /* Spec blank on revid */
19519921b23SOrion Hodson 	{ 0x4e534343, 0x00, 0, "LM4543",	0 }, /* Ditto */
19619921b23SOrion Hodson 	{ 0x4e534346, 0x00, 0, "LM4546A",	0 },
19719921b23SOrion Hodson 	{ 0x4e534348, 0x00, 0, "LM4548A",	0 },
1983c6b655dSMathew Kanner 	{ 0x4e534331, 0x00, 0, "LM4549",	0 },
19919921b23SOrion Hodson 	{ 0x4e534349, 0x00, 0, "LM4549A",	0 },
20019921b23SOrion Hodson 	{ 0x4e534350, 0x00, 0, "LM4550",	0 },
20119921b23SOrion Hodson 	{ 0x50534301, 0x00, 0, "UCB1510",	0 },
20219921b23SOrion Hodson 	{ 0x50534304, 0x00, 0, "UCB1400",	0 },
20319921b23SOrion Hodson 	{ 0x83847600, 0x00, 0, "STAC9700/83/84",	0 },
20419921b23SOrion Hodson 	{ 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
20519921b23SOrion Hodson 	{ 0x83847605, 0x00, 0, "STAC9704",	0 },
20619921b23SOrion Hodson 	{ 0x83847608, 0x00, 0, "STAC9708/11",	0 },
20719921b23SOrion Hodson 	{ 0x83847609, 0x00, 0, "STAC9721/23",	0 },
20819921b23SOrion Hodson 	{ 0x83847644, 0x00, 0, "STAC9744/45",	0 },
20919921b23SOrion Hodson 	{ 0x83847650, 0x00, 0, "STAC9750/51",	0 },
21019921b23SOrion Hodson 	{ 0x83847652, 0x00, 0, "STAC9752/53",	0 },
21119921b23SOrion Hodson 	{ 0x83847656, 0x00, 0, "STAC9756/57",	0 },
21219921b23SOrion Hodson 	{ 0x83847658, 0x00, 0, "STAC9758/59",	0 },
21319921b23SOrion Hodson 	{ 0x83847660, 0x00, 0, "STAC9760/61",	0 }, /* Extrapolated */
21419921b23SOrion Hodson 	{ 0x83847662, 0x00, 0, "STAC9762/63",	0 }, /* Extrapolated */
215cb44f623SAlexander Leidinger 	{ 0x83847666, 0x00, 0, "STAC9766/67",	0 },
21619921b23SOrion Hodson 	{ 0x53494c22, 0x00, 0, "Si3036",	0 },
21719921b23SOrion Hodson 	{ 0x53494c23, 0x00, 0, "Si3038",	0 },
21819921b23SOrion Hodson 	{ 0x54524103, 0x00, 0, "TR28023",	0 }, /* Extrapolated */
21919921b23SOrion Hodson 	{ 0x54524106, 0x00, 0, "TR28026",	0 },
22019921b23SOrion Hodson 	{ 0x54524108, 0x00, 0, "TR28028",	0 },
22119921b23SOrion Hodson 	{ 0x54524123, 0x00, 0, "TR28602",	0 },
2223c6b655dSMathew Kanner 	{ 0x54524e03, 0x07, 0, "TLV320AIC27",	0 },
2233c6b655dSMathew Kanner 	{ 0x54584e20, 0x00, 0, "TLC320AD90",	0 },
22419921b23SOrion Hodson 	{ 0x56494161, 0x00, 0, "VIA1612A",      0 },
22580138937SAriff Abdullah 	{ 0x56494170, 0x00, 0, "VIA1617A",      0 },
22619921b23SOrion Hodson 	{ 0x574d4c00, 0x00, 0, "WM9701A",	0 },
22719921b23SOrion Hodson 	{ 0x574d4c03, 0x00, 0, "WM9703/4/7/8",	0 },
22819921b23SOrion Hodson 	{ 0x574d4c04, 0x00, 0, "WM9704Q",	0 },
22919921b23SOrion Hodson 	{ 0x574d4c05, 0x00, 0, "WM9705/10",	0 },
2303c6b655dSMathew Kanner 	{ 0x574d4d09, 0x00, 0, "WM9709",	0 },
2313c6b655dSMathew Kanner 	{ 0x574d4c12, 0x00, 0, "WM9711/12",	0 }, /* XXX: patch needed */
2323c6b655dSMathew Kanner 	{ 0x57454301, 0x00, 0, "W83971D",	0 },
23319921b23SOrion Hodson 	{ 0x594d4800, 0x00, 0, "YMF743",	0 },
23419921b23SOrion Hodson 	{ 0x594d4802, 0x00, 0, "YMF752",	0 },
23519921b23SOrion Hodson 	{ 0x594d4803, 0x00, 0, "YMF753",	0 },
23686336196SAlexander Leidinger 	/*
23786336196SAlexander Leidinger 	 * XXX This is a fluke, really! The real codec
23886336196SAlexander Leidinger 	 * should be STAC9704, not this! This should be
23986336196SAlexander Leidinger 	 * removed someday!
24086336196SAlexander Leidinger 	 */
2416f0182bdSOrion Hodson 	{ 0x01408384, 0x00, 0, "EV1938",	0 },
24219921b23SOrion Hodson 	{ 0, 0, 0, NULL, 0 }
243987e5972SCameron Grant };
244987e5972SCameron Grant 
245987e5972SCameron Grant static char *ac97enhancement[] = {
24604553e63SCameron Grant 	"no 3D Stereo Enhancement",
247987e5972SCameron Grant 	"Analog Devices Phat Stereo",
248987e5972SCameron Grant 	"Creative Stereo Enhancement",
249987e5972SCameron Grant 	"National Semi 3D Stereo Enhancement",
250987e5972SCameron Grant 	"Yamaha Ymersion",
251987e5972SCameron Grant 	"BBE 3D Stereo Enhancement",
252987e5972SCameron Grant 	"Crystal Semi 3D Stereo Enhancement",
253987e5972SCameron Grant 	"Qsound QXpander",
254987e5972SCameron Grant 	"Spatializer 3D Stereo Enhancement",
255987e5972SCameron Grant 	"SRS 3D Stereo Enhancement",
256987e5972SCameron Grant 	"Platform Tech 3D Stereo Enhancement",
257987e5972SCameron Grant 	"AKM 3D Audio",
258987e5972SCameron Grant 	"Aureal Stereo Enhancement",
259987e5972SCameron Grant 	"Aztech 3D Enhancement",
260987e5972SCameron Grant 	"Binaura 3D Audio Enhancement",
261987e5972SCameron Grant 	"ESS Technology Stereo Enhancement",
262987e5972SCameron Grant 	"Harman International VMAx",
263987e5972SCameron Grant 	"Nvidea 3D Stereo Enhancement",
264987e5972SCameron Grant 	"Philips Incredible Sound",
265987e5972SCameron Grant 	"Texas Instruments 3D Stereo Enhancement",
266987e5972SCameron Grant 	"VLSI Technology 3D Stereo Enhancement",
267987e5972SCameron Grant 	"TriTech 3D Stereo Enhancement",
268987e5972SCameron Grant 	"Realtek 3D Stereo Enhancement",
269987e5972SCameron Grant 	"Samsung 3D Stereo Enhancement",
270987e5972SCameron Grant 	"Wolfson Microelectronics 3D Enhancement",
271987e5972SCameron Grant 	"Delta Integration 3D Enhancement",
272987e5972SCameron Grant 	"SigmaTel 3D Enhancement",
273987e5972SCameron Grant 	"Reserved 27",
274987e5972SCameron Grant 	"Rockwell 3D Stereo Enhancement",
275987e5972SCameron Grant 	"Reserved 29",
276987e5972SCameron Grant 	"Reserved 30",
277987e5972SCameron Grant 	"Reserved 31"
278987e5972SCameron Grant };
279987e5972SCameron Grant 
280987e5972SCameron Grant static char *ac97feature[] = {
281987e5972SCameron Grant 	"mic channel",
282987e5972SCameron Grant 	"reserved",
283987e5972SCameron Grant 	"tone",
284987e5972SCameron Grant 	"simulated stereo",
285987e5972SCameron Grant 	"headphone",
286987e5972SCameron Grant 	"bass boost",
287987e5972SCameron Grant 	"18 bit DAC",
288987e5972SCameron Grant 	"20 bit DAC",
289987e5972SCameron Grant 	"18 bit ADC",
290987e5972SCameron Grant 	"20 bit ADC"
291987e5972SCameron Grant };
292987e5972SCameron Grant 
29339004e69SCameron Grant static char *ac97extfeature[] = {
29439004e69SCameron Grant 	"variable rate PCM",
29539004e69SCameron Grant 	"double rate PCM",
29639004e69SCameron Grant 	"reserved 1",
29739004e69SCameron Grant 	"variable rate mic",
29839004e69SCameron Grant 	"reserved 2",
29939004e69SCameron Grant 	"reserved 3",
30039004e69SCameron Grant 	"center DAC",
30139004e69SCameron Grant 	"surround DAC",
30239004e69SCameron Grant 	"LFE DAC",
30339004e69SCameron Grant 	"AMAP",
30439004e69SCameron Grant 	"reserved 4",
30539004e69SCameron Grant 	"reserved 5",
30639004e69SCameron Grant 	"reserved 6",
30739004e69SCameron Grant 	"reserved 7",
30839004e69SCameron Grant };
30939004e69SCameron Grant 
310f9eb1409SOrion Hodson u_int16_t
311f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg)
31239004e69SCameron Grant {
31386336196SAlexander Leidinger 	if (codec->flags & AC97_F_RDCD_BUG) {
31486336196SAlexander Leidinger 		u_int16_t i[2], j = 100;
31586336196SAlexander Leidinger 
31686336196SAlexander Leidinger 		i[0] = AC97_READ(codec->methods, codec->devinfo, reg);
31786336196SAlexander Leidinger 		i[1] = AC97_READ(codec->methods, codec->devinfo, reg);
31886336196SAlexander Leidinger 		while (i[0] != i[1] && j)
31986336196SAlexander Leidinger 			i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg);
32086336196SAlexander Leidinger #if 0
32186336196SAlexander Leidinger 		if (j < 100) {
32286336196SAlexander Leidinger 			device_printf(codec->dev, "%s(): Inconsistent register value at"
32386336196SAlexander Leidinger 					" 0x%08x (retry: %d)\n", __func__, reg, 100 - j);
32486336196SAlexander Leidinger 		}
32586336196SAlexander Leidinger #endif
32686336196SAlexander Leidinger 		return i[!(j & 1)];
32786336196SAlexander Leidinger 	}
3280f55ac6cSCameron Grant 	return AC97_READ(codec->methods, codec->devinfo, reg);
32939004e69SCameron Grant }
33039004e69SCameron Grant 
331f9eb1409SOrion Hodson void
332f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
33339004e69SCameron Grant {
3340f55ac6cSCameron Grant 	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
33539004e69SCameron Grant }
33639004e69SCameron Grant 
337c6d4b83aSOrion Hodson static void
338c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec)
339c6d4b83aSOrion Hodson {
340c6d4b83aSOrion Hodson 	u_int32_t i, ps;
341f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_RESET, 0);
342c6d4b83aSOrion Hodson 	for (i = 0; i < 500; i++) {
343f9eb1409SOrion Hodson 		ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
344c6d4b83aSOrion Hodson 		if (ps == AC97_POWER_STATUS)
345c6d4b83aSOrion Hodson 			return;
346c6d4b83aSOrion Hodson 		DELAY(1000);
347c6d4b83aSOrion Hodson 	}
348a825c6e5SOrion Hodson 	device_printf(codec->dev, "AC97 reset timed out.\n");
349c6d4b83aSOrion Hodson }
350c6d4b83aSOrion Hodson 
35139004e69SCameron Grant int
35239004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate)
35339004e69SCameron Grant {
35439004e69SCameron Grant 	u_int16_t v;
35539004e69SCameron Grant 
35639004e69SCameron Grant 	switch(which) {
35739004e69SCameron Grant 	case AC97_REGEXT_FDACRATE:
35839004e69SCameron Grant 	case AC97_REGEXT_SDACRATE:
35939004e69SCameron Grant 	case AC97_REGEXT_LDACRATE:
36039004e69SCameron Grant 	case AC97_REGEXT_LADCRATE:
36139004e69SCameron Grant 	case AC97_REGEXT_MADCRATE:
36239004e69SCameron Grant 		break;
36339004e69SCameron Grant 
36439004e69SCameron Grant 	default:
36539004e69SCameron Grant 		return -1;
36639004e69SCameron Grant 	}
36739004e69SCameron Grant 
36866ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
36939004e69SCameron Grant 	if (rate != 0) {
37039004e69SCameron Grant 		v = rate;
37139004e69SCameron Grant 		if (codec->extstat & AC97_EXTCAP_DRA)
37239004e69SCameron Grant 			v >>= 1;
373f9eb1409SOrion Hodson 		ac97_wrcd(codec, which, v);
37439004e69SCameron Grant 	}
375f9eb1409SOrion Hodson 	v = ac97_rdcd(codec, which);
37639004e69SCameron Grant 	if (codec->extstat & AC97_EXTCAP_DRA)
37739004e69SCameron Grant 		v <<= 1;
37866ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
37939004e69SCameron Grant 	return v;
38039004e69SCameron Grant }
38139004e69SCameron Grant 
38239004e69SCameron Grant int
38339004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
38439004e69SCameron Grant {
38539004e69SCameron Grant 	mode &= AC97_EXTCAPS;
386647fbfebSOrion Hodson 	if ((mode & ~codec->extcaps) != 0) {
387647fbfebSOrion Hodson 		device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
388647fbfebSOrion Hodson 			      mode);
38939004e69SCameron Grant 		return -1;
390647fbfebSOrion Hodson 	}
39166ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
392f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
393f9eb1409SOrion Hodson 	codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
39466ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
39539004e69SCameron Grant 	return (mode == codec->extstat)? 0 : -1;
39639004e69SCameron Grant }
39739004e69SCameron Grant 
3989ec437a3SCameron Grant u_int16_t
3999ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec)
4009ec437a3SCameron Grant {
4019ec437a3SCameron Grant 	return codec->extstat;
4029ec437a3SCameron Grant }
4039ec437a3SCameron Grant 
4049ec437a3SCameron Grant u_int16_t
4059ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec)
4069ec437a3SCameron Grant {
4079ec437a3SCameron Grant 	return codec->extcaps;
4089ec437a3SCameron Grant }
4099ec437a3SCameron Grant 
4105d91ad67SCameron Grant u_int16_t
4115d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec)
4125d91ad67SCameron Grant {
4135d91ad67SCameron Grant 	return codec->caps;
4145d91ad67SCameron Grant }
4155d91ad67SCameron Grant 
416fd7390d6SAriff Abdullah u_int32_t
417fd7390d6SAriff Abdullah ac97_getsubvendor(struct ac97_info *codec)
418fd7390d6SAriff Abdullah {
419fd7390d6SAriff Abdullah 	return codec->subvendor;
420fd7390d6SAriff Abdullah }
421fd7390d6SAriff Abdullah 
422987e5972SCameron Grant static int
423987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel)
424987e5972SCameron Grant {
425987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
426341f16ccSCameron Grant 
427987e5972SCameron Grant 	if (e->recidx > 0) {
428987e5972SCameron Grant 		int val = e->recidx - 1;
429987e5972SCameron Grant 		val |= val << 8;
43066ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
431f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REG_RECSEL, val);
43266ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
433987e5972SCameron Grant 		return 0;
43439004e69SCameron Grant 	} else
43539004e69SCameron Grant 		return -1;
436987e5972SCameron Grant }
437987e5972SCameron Grant 
438987e5972SCameron Grant static int
439987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
440987e5972SCameron Grant {
441987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
442341f16ccSCameron Grant 
443341f16ccSCameron Grant 	if (e->reg && e->enable && e->bits) {
44496524a52SOrion Hodson 		int mask, max, val, reg;
44596524a52SOrion Hodson 
44696524a52SOrion Hodson 		reg = (e->reg >= 0) ? e->reg : -e->reg;	/* AC97 register    */
44796524a52SOrion Hodson 		max = (1 << e->bits) - 1;		/* actual range	    */
44896524a52SOrion Hodson 		mask = (max << 8) | max;		/* bits of interest */
449987e5972SCameron Grant 
45039004e69SCameron Grant 		if (!e->stereo)
45139004e69SCameron Grant 			right = left;
45296524a52SOrion Hodson 
45396524a52SOrion Hodson 		/*
45496524a52SOrion Hodson 		 * Invert the range if the polarity requires so,
45596524a52SOrion Hodson 		 * then scale to 0..max-1 to compute the value to
45696524a52SOrion Hodson 		 * write into the codec, and scale back to 0..100
45796524a52SOrion Hodson 		 * for the return value.
45896524a52SOrion Hodson 		 */
459987e5972SCameron Grant 		if (e->reg > 0) {
460987e5972SCameron Grant 			left = 100 - left;
461987e5972SCameron Grant 			right = 100 - right;
462987e5972SCameron Grant 		}
463987e5972SCameron Grant 
464987e5972SCameron Grant 		left = (left * max) / 100;
465987e5972SCameron Grant 		right = (right * max) / 100;
466987e5972SCameron Grant 
467987e5972SCameron Grant 		val = (left << 8) | right;
468987e5972SCameron Grant 
469987e5972SCameron Grant 		left = (left * 100) / max;
470987e5972SCameron Grant 		right = (right * 100) / max;
471987e5972SCameron Grant 
472987e5972SCameron Grant 		if (e->reg > 0) {
473987e5972SCameron Grant 			left = 100 - left;
474987e5972SCameron Grant 			right = 100 - right;
475987e5972SCameron Grant 		}
476987e5972SCameron Grant 
47796524a52SOrion Hodson 		/*
47896524a52SOrion Hodson 		 * For mono controls, trim val and mask, also taking
47996524a52SOrion Hodson 		 * care of e->ofs (offset of control field).
48096524a52SOrion Hodson 		 */
48196524a52SOrion Hodson 		if (e->ofs) {
482987e5972SCameron Grant 			val &= max;
483987e5972SCameron Grant 			val <<= e->ofs;
48496524a52SOrion Hodson 			mask = (max << e->ofs);
48596524a52SOrion Hodson 		}
48696524a52SOrion Hodson 
48796524a52SOrion Hodson 		/*
48896524a52SOrion Hodson 		 * If we have a mute bit, add it to the mask and
48996524a52SOrion Hodson 		 * update val and set mute if both channels require a
49096524a52SOrion Hodson 		 * zero volume.
49196524a52SOrion Hodson 		 */
49296524a52SOrion Hodson 		if (e->mute == 1) {
49396524a52SOrion Hodson 			mask |= AC97_MUTE;
49496524a52SOrion Hodson 			if (left == 0 && right == 0)
49596524a52SOrion Hodson 				val = AC97_MUTE;
49696524a52SOrion Hodson 		}
49796524a52SOrion Hodson 
49896524a52SOrion Hodson 		/*
49996524a52SOrion Hodson 		 * If the mask bit is set, do not alter the other bits.
50096524a52SOrion Hodson 		 */
50196524a52SOrion Hodson 		snd_mtxlock(codec->lock);
502987e5972SCameron Grant 		if (e->mask) {
50386336196SAlexander Leidinger 			int cur = ac97_rdcd(codec, reg);
50496524a52SOrion Hodson 			val |= cur & ~(mask);
505987e5972SCameron Grant 		}
506f9eb1409SOrion Hodson 		ac97_wrcd(codec, reg, val);
50766ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
508987e5972SCameron Grant 		return left | (right << 8);
509341f16ccSCameron Grant 	} else {
51086336196SAlexander Leidinger #if 0
51186336196SAlexander Leidinger 		printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable);
51286336196SAlexander Leidinger #endif
51339004e69SCameron Grant 		return -1;
514987e5972SCameron Grant 	}
515341f16ccSCameron Grant }
516987e5972SCameron Grant 
517108082c4SOrion Hodson static void
518108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec)
519108082c4SOrion Hodson {
520cfd5696dSOrion Hodson 	int keep_ogain;
521cfd5696dSOrion Hodson 
522a52604cfSOrion Hodson 	/*
523cfd5696dSOrion Hodson 	 * By default, The ac97 aux_out register (0x04) corresponds to OSS's
524cfd5696dSOrion Hodson 	 * OGAIN setting.
525a52604cfSOrion Hodson 	 *
526cfd5696dSOrion Hodson 	 * We first check whether aux_out is a valid register.  If not
527cfd5696dSOrion Hodson 	 * we may not want to keep ogain.
528a52604cfSOrion Hodson 	 */
529cfd5696dSOrion Hodson 	keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000;
530a52604cfSOrion Hodson 
531a52604cfSOrion Hodson 	/*
532a52604cfSOrion Hodson 	 * Determine what AUX_OUT really means, it can be:
533108082c4SOrion Hodson 	 *
534108082c4SOrion Hodson 	 * 1. Headphone out.
535108082c4SOrion Hodson 	 * 2. 4-Channel Out
536108082c4SOrion Hodson 	 * 3. True line level out (effectively master volume).
537108082c4SOrion Hodson 	 *
538108082c4SOrion Hodson 	 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
539108082c4SOrion Hodson 	 */
540a52604cfSOrion Hodson 	if (codec->extcaps & AC97_EXTCAP_SDAC &&
541f9eb1409SOrion Hodson 	    ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
542cfd5696dSOrion Hodson 		codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND;
543cfd5696dSOrion Hodson 		keep_ogain = 1;
544cfd5696dSOrion Hodson 	}
545cfd5696dSOrion Hodson 
546cfd5696dSOrion Hodson 	if (keep_ogain == 0) {
547cfd5696dSOrion Hodson 		bzero(&codec->mix[SOUND_MIXER_OGAIN],
548cfd5696dSOrion Hodson 		      sizeof(codec->mix[SOUND_MIXER_OGAIN]));
549108082c4SOrion Hodson 	}
550a52604cfSOrion Hodson }
551a52604cfSOrion Hodson 
552a52604cfSOrion Hodson static void
553a52604cfSOrion Hodson ac97_fix_tone(struct ac97_info *codec)
554a52604cfSOrion Hodson {
555f0c4d272SAriff Abdullah 	/*
556f0c4d272SAriff Abdullah 	 * YMF chips does not indicate tone and 3D enhancement capability
557f0c4d272SAriff Abdullah 	 * in the AC97_REG_RESET register.
558f0c4d272SAriff Abdullah 	 */
559f0c4d272SAriff Abdullah 	switch (codec->id) {
560f0c4d272SAriff Abdullah 	case 0x594d4800:	/* YMF743 */
561f0c4d272SAriff Abdullah 	case 0x594d4803:	/* YMF753 */
562f0c4d272SAriff Abdullah 		codec->caps |= AC97_CAP_TONE;
563f0c4d272SAriff Abdullah 		codec->se |= 0x04;
564f0c4d272SAriff Abdullah 		break;
565f0c4d272SAriff Abdullah 	case 0x594d4802:	/* YMF752 */
566f0c4d272SAriff Abdullah 		codec->se |= 0x04;
567f0c4d272SAriff Abdullah 		break;
568f0c4d272SAriff Abdullah 	default:
569f0c4d272SAriff Abdullah 		break;
570f0c4d272SAriff Abdullah 	}
571f0c4d272SAriff Abdullah 
572a52604cfSOrion Hodson 	/* Hide treble and bass if they don't exist */
573a52604cfSOrion Hodson 	if ((codec->caps & AC97_CAP_TONE) == 0) {
574a52604cfSOrion Hodson 		bzero(&codec->mix[SOUND_MIXER_BASS],
575a52604cfSOrion Hodson 		      sizeof(codec->mix[SOUND_MIXER_BASS]));
576a52604cfSOrion Hodson 		bzero(&codec->mix[SOUND_MIXER_TREBLE],
577a52604cfSOrion Hodson 		      sizeof(codec->mix[SOUND_MIXER_TREBLE]));
578a52604cfSOrion Hodson 	}
579108082c4SOrion Hodson }
580108082c4SOrion Hodson 
58119921b23SOrion Hodson static const char*
58219921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
58319921b23SOrion Hodson {
58419921b23SOrion Hodson 	if (cname == NULL) {
58519921b23SOrion Hodson 		sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
58619921b23SOrion Hodson 		return buf;
58719921b23SOrion Hodson 	}
58819921b23SOrion Hodson 
58919921b23SOrion Hodson 	if (vname == NULL) vname = "Unknown";
59019921b23SOrion Hodson 
59119921b23SOrion Hodson 	if (bootverbose) {
59219921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
59319921b23SOrion Hodson 	} else {
59419921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec", vname, cname);
59519921b23SOrion Hodson 	}
59619921b23SOrion Hodson 	return buf;
59719921b23SOrion Hodson }
59819921b23SOrion Hodson 
599987e5972SCameron Grant static unsigned
60039004e69SCameron Grant ac97_initmixer(struct ac97_info *codec)
601987e5972SCameron Grant {
602f9eb1409SOrion Hodson 	ac97_patch codec_patch;
60319921b23SOrion Hodson 	const char *cname, *vname;
60419921b23SOrion Hodson 	char desc[80];
60519921b23SOrion Hodson 	u_int8_t model, step;
60686336196SAlexander Leidinger 	unsigned i, j, k, bit, old;
607987e5972SCameron Grant 	u_int32_t id;
60886336196SAlexander Leidinger 	int reg;
609987e5972SCameron Grant 
61066ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
6110f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
612cd2c103aSCameron Grant 	if (codec->count == 0) {
61304553e63SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
61466ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
61504553e63SCameron Grant 		return ENODEV;
61604553e63SCameron Grant 	}
6179ec437a3SCameron Grant 
618f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
619c6d4b83aSOrion Hodson 	ac97_reset(codec);
620f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
621987e5972SCameron Grant 
622f9eb1409SOrion Hodson 	i = ac97_rdcd(codec, AC97_REG_RESET);
62386336196SAlexander Leidinger 	j = ac97_rdcd(codec, AC97_REG_RESET);
62469f6d261SAriff Abdullah 	k = ac97_rdcd(codec, AC97_REG_RESET);
62586336196SAlexander Leidinger 	/*
62686336196SAlexander Leidinger 	 * Let see if this codec can return consistent value.
62786336196SAlexander Leidinger 	 * If not, turn on aggressive read workaround
62886336196SAlexander Leidinger 	 * (STAC9704 comes in mind).
62986336196SAlexander Leidinger 	 */
63069f6d261SAriff Abdullah 	if (i != j || j != k) {
63186336196SAlexander Leidinger 		codec->flags |= AC97_F_RDCD_BUG;
63286336196SAlexander Leidinger 		i = ac97_rdcd(codec, AC97_REG_RESET);
63386336196SAlexander Leidinger 	}
634987e5972SCameron Grant 	codec->caps = i & 0x03ff;
635987e5972SCameron Grant 	codec->se =  (i & 0x7c00) >> 10;
636987e5972SCameron Grant 
637f9eb1409SOrion Hodson 	id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
638e620d959SCameron Grant 	if (id == 0 || id == 0xffffffff) {
639e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
64066ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
641e620d959SCameron Grant 		return ENODEV;
642e620d959SCameron Grant 	}
6436b4b88f7SCameron Grant 
64419921b23SOrion Hodson 	codec->id = id;
645fd7390d6SAriff Abdullah 	codec->subvendor = (u_int32_t)pci_get_subdevice(codec->dev) << 16;
646fd7390d6SAriff Abdullah 	codec->subvendor |= (u_int32_t)pci_get_subvendor(codec->dev) &
647fd7390d6SAriff Abdullah 	    0x0000ffff;
648cd2c103aSCameron Grant 	codec->noext = 0;
649f9eb1409SOrion Hodson 	codec_patch = NULL;
65019921b23SOrion Hodson 
65119921b23SOrion Hodson 	cname = NULL;
65219921b23SOrion Hodson 	model = step = 0;
653cd2c103aSCameron Grant 	for (i = 0; ac97codecid[i].id; i++) {
65419921b23SOrion Hodson 		u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
65519921b23SOrion Hodson 		if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
656cd2c103aSCameron Grant 			codec->noext = ac97codecid[i].noext;
657f9eb1409SOrion Hodson 			codec_patch = ac97codecid[i].patch;
65819921b23SOrion Hodson 			cname = ac97codecid[i].name;
65919921b23SOrion Hodson 			model = (id & modelmask) & 0xff;
66019921b23SOrion Hodson 			step = (id & ~modelmask) & 0xff;
66119921b23SOrion Hodson 			break;
66219921b23SOrion Hodson 		}
66319921b23SOrion Hodson 	}
66419921b23SOrion Hodson 
66519921b23SOrion Hodson 	vname = NULL;
66619921b23SOrion Hodson 	for (i = 0; ac97vendorid[i].id; i++) {
66719921b23SOrion Hodson 		if (ac97vendorid[i].id == (id & 0xffffff00)) {
66819921b23SOrion Hodson 			vname = ac97vendorid[i].name;
66919921b23SOrion Hodson 			break;
670cd2c103aSCameron Grant 		}
671cd2c103aSCameron Grant 	}
6726b4b88f7SCameron Grant 
673cd2c103aSCameron Grant 	codec->extcaps = 0;
674cd2c103aSCameron Grant 	codec->extid = 0;
675cd2c103aSCameron Grant 	codec->extstat = 0;
6766a6ee5bbSCameron Grant 	if (!codec->noext) {
677f9eb1409SOrion Hodson 		i = ac97_rdcd(codec, AC97_REGEXT_ID);
6786a6ee5bbSCameron Grant 		if (i != 0xffff) {
67939004e69SCameron Grant 			codec->extcaps = i & 0x3fff;
68039004e69SCameron Grant 			codec->extid =  (i & 0xc000) >> 14;
681f9eb1409SOrion Hodson 			codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
6826b4b88f7SCameron Grant 		}
6836a6ee5bbSCameron Grant 	}
684987e5972SCameron Grant 
68569f6d261SAriff Abdullah 	for (i = 0; i < AC97_MIXER_SIZE; i++) {
686108082c4SOrion Hodson 		codec->mix[i] = ac97mixtable_default[i];
687108082c4SOrion Hodson 	}
688108082c4SOrion Hodson 	ac97_fix_auxout(codec);
689a52604cfSOrion Hodson 	ac97_fix_tone(codec);
690f9eb1409SOrion Hodson 	if (codec_patch)
691f9eb1409SOrion Hodson 		codec_patch(codec);
692108082c4SOrion Hodson 
69369f6d261SAriff Abdullah 	for (i = 0; i < AC97_MIXER_SIZE; i++) {
69433c878f0SCameron Grant 		k = codec->noext? codec->mix[i].enable : 1;
69586336196SAlexander Leidinger 		reg = codec->mix[i].reg;
69686336196SAlexander Leidinger 		if (reg < 0)
69786336196SAlexander Leidinger 			reg = -reg;
69886336196SAlexander Leidinger 		if (k && reg) {
69986336196SAlexander Leidinger 			j = old = ac97_rdcd(codec, reg);
70086336196SAlexander Leidinger 			/*
70186336196SAlexander Leidinger 			 * Test for mute bit (except for AC97_MIX_TONE,
70286336196SAlexander Leidinger 			 * where we simply assume it as available).
70386336196SAlexander Leidinger 			 */
70486336196SAlexander Leidinger 			if (codec->mix[i].mute) {
70586336196SAlexander Leidinger 				ac97_wrcd(codec, reg, j | 0x8000);
70686336196SAlexander Leidinger 				j = ac97_rdcd(codec, reg);
70786336196SAlexander Leidinger 			} else
708fe435202SAlexander Leidinger 				j |= 0x8000;
70986336196SAlexander Leidinger 			if ((j & 0x8000)) {
71086336196SAlexander Leidinger 				/*
71186336196SAlexander Leidinger 				 * Test whether the control width should be
71286336196SAlexander Leidinger 				 * 4, 5 or 6 bit. For 5bit register, we should
71386336196SAlexander Leidinger 				 * test it whether it's really 5 or 6bit. Leave
71486336196SAlexander Leidinger 				 * 4bit register alone, because sometimes an
71586336196SAlexander Leidinger 				 * attempt to write past 4th bit may cause
71686336196SAlexander Leidinger 				 * incorrect result especially for AC97_MIX_BEEP
71786336196SAlexander Leidinger 				 * (ac97 2.3).
71886336196SAlexander Leidinger 				 */
71986336196SAlexander Leidinger 				bit = codec->mix[i].bits;
72086336196SAlexander Leidinger 				if (bit == 5)
72186336196SAlexander Leidinger 					bit++;
72286336196SAlexander Leidinger 				j = ((1 << bit) - 1) << codec->mix[i].ofs;
72386336196SAlexander Leidinger 				ac97_wrcd(codec, reg,
72486336196SAlexander Leidinger 					j | (codec->mix[i].mute ? 0x8000 : 0));
72586336196SAlexander Leidinger 				k = ac97_rdcd(codec, reg) & j;
72686336196SAlexander Leidinger 				k >>= codec->mix[i].ofs;
72786336196SAlexander Leidinger 				if (reg == AC97_MIX_TONE &&
72886336196SAlexander Leidinger 							((k & 0x0001) == 0x0000))
729fe435202SAlexander Leidinger 					k >>= 1;
73086336196SAlexander Leidinger 				for (j = 0; k >> j; j++)
73186336196SAlexander Leidinger 					;
732fe435202SAlexander Leidinger 				if (j != 0) {
733fe435202SAlexander Leidinger #if 0
73486336196SAlexander Leidinger 					device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n",
73586336196SAlexander Leidinger 						i, k, bit, codec->mix[i].bits, j);
736fe435202SAlexander Leidinger #endif
73786336196SAlexander Leidinger 					codec->mix[i].enable = 1;
73886336196SAlexander Leidinger 					codec->mix[i].bits = j;
739eaf70083SAriff Abdullah 				} else if (reg == AC97_MIX_BEEP) {
740eaf70083SAriff Abdullah 					/*
741eaf70083SAriff Abdullah 					 * Few codec such as CX20468-21 does
742eaf70083SAriff Abdullah 					 * have this control register, although
743eaf70083SAriff Abdullah 					 * the only usable part is the mute bit.
744eaf70083SAriff Abdullah 					 */
745eaf70083SAriff Abdullah 					codec->mix[i].enable = 1;
746fe435202SAlexander Leidinger 				} else
747fe435202SAlexander Leidinger 					codec->mix[i].enable = 0;
748fe435202SAlexander Leidinger 			} else
749fe435202SAlexander Leidinger 				codec->mix[i].enable = 0;
75086336196SAlexander Leidinger 			ac97_wrcd(codec, reg, old);
751fe435202SAlexander Leidinger 		}
752fe435202SAlexander Leidinger #if 0
753fe435202SAlexander Leidinger 		printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits);
754fe435202SAlexander Leidinger #endif
755341f16ccSCameron Grant 	}
756987e5972SCameron Grant 
75719921b23SOrion Hodson 	device_printf(codec->dev, "<%s>\n",
75819921b23SOrion Hodson 		      ac97_hw_desc(codec->id, vname, cname, desc));
759a825c6e5SOrion Hodson 
760987e5972SCameron Grant 	if (bootverbose) {
76186336196SAlexander Leidinger 		if (codec->flags & AC97_F_RDCD_BUG)
76286336196SAlexander Leidinger 			device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n");
76319921b23SOrion Hodson 		device_printf(codec->dev, "Codec features ");
76439004e69SCameron Grant 		for (i = j = 0; i < 10; i++)
76539004e69SCameron Grant 			if (codec->caps & (1 << i))
76639004e69SCameron Grant 				printf("%s%s", j++? ", " : "", ac97feature[i]);
76739004e69SCameron Grant 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
768987e5972SCameron Grant 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
76939004e69SCameron Grant 
77039004e69SCameron Grant 		if (codec->extcaps != 0 || codec->extid) {
77119921b23SOrion Hodson 			device_printf(codec->dev, "%s codec",
77219921b23SOrion Hodson 				      codec->extid? "Secondary" : "Primary");
77339004e69SCameron Grant 			if (codec->extcaps)
77439004e69SCameron Grant 				printf(" extended features ");
77539004e69SCameron Grant 			for (i = j = 0; i < 14; i++)
77639004e69SCameron Grant 				if (codec->extcaps & (1 << i))
77739004e69SCameron Grant 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
77839004e69SCameron Grant 			printf("\n");
77939004e69SCameron Grant 		}
780987e5972SCameron Grant 	}
781987e5972SCameron Grant 
782fe435202SAlexander Leidinger 	i = 0;
783fe435202SAlexander Leidinger 	while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) {
784fe435202SAlexander Leidinger 		if (++i == 100) {
78503a00905SCameron Grant 			device_printf(codec->dev, "ac97 codec reports dac not ready\n");
786fe435202SAlexander Leidinger 			break;
787fe435202SAlexander Leidinger 		}
788fe435202SAlexander Leidinger 		DELAY(1000);
789fe435202SAlexander Leidinger 	}
790fe435202SAlexander Leidinger 	if (bootverbose)
791fe435202SAlexander Leidinger 		device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i);
79266ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
793987e5972SCameron Grant 	return 0;
794987e5972SCameron Grant }
795987e5972SCameron Grant 
7969ec437a3SCameron Grant static unsigned
7979ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec)
7989ec437a3SCameron Grant {
79966ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
8000f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
8019ec437a3SCameron Grant 	if (codec->count == 0) {
8029ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
80366ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
8049ec437a3SCameron Grant 		return ENODEV;
8059ec437a3SCameron Grant 	}
8069ec437a3SCameron Grant 
807f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
808c6d4b83aSOrion Hodson 	ac97_reset(codec);
809f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
8109ec437a3SCameron Grant 
8119ec437a3SCameron Grant 	if (!codec->noext) {
812f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
813f9eb1409SOrion Hodson 		if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
814b60e55dbSGuido van Rooij 		    != codec->extstat)
8159ec437a3SCameron Grant 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
816b60e55dbSGuido van Rooij 				      codec->extstat,
817f9eb1409SOrion Hodson 				      ac97_rdcd(codec, AC97_REGEXT_STAT) &
818b60e55dbSGuido van Rooij 				      AC97_EXTCAPS);
8199ec437a3SCameron Grant 	}
8209ec437a3SCameron Grant 
821f9eb1409SOrion Hodson 	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
8229ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
82366ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
8249ec437a3SCameron Grant 	return 0;
8259ec437a3SCameron Grant }
8269ec437a3SCameron Grant 
827987e5972SCameron Grant struct ac97_info *
8280f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
829987e5972SCameron Grant {
830987e5972SCameron Grant 	struct ac97_info *codec;
83190da2b28SAriff Abdullah 	int i;
832987e5972SCameron Grant 
833082f6383SAriff Abdullah 	codec = malloc(sizeof(*codec), M_AC97, M_WAITOK | M_ZERO);
83469f6d261SAriff Abdullah 	snprintf(codec->name, sizeof(codec->name), "%s:ac97",
83569f6d261SAriff Abdullah 	    device_get_nameunit(dev));
836489c22ebSJohn Baldwin 	codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
8377699548fSAriff Abdullah 	codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO);
8380f55ac6cSCameron Grant 	codec->dev = dev;
8390f55ac6cSCameron Grant 	codec->devinfo = devinfo;
84079bb7d52SCameron Grant 	codec->flags = 0;
84190da2b28SAriff Abdullah 
8427699548fSAriff Abdullah 	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
84390da2b28SAriff Abdullah 	    "eapdinv", &i) == 0 && i != 0)
8447699548fSAriff Abdullah 		codec->flags |= AC97_F_EAPD_INV;
84590da2b28SAriff Abdullah 
84690da2b28SAriff Abdullah 	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
84790da2b28SAriff Abdullah 	    "softpcmvol", &i) == 0 && i != 0)
84890da2b28SAriff Abdullah 		pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL);
84990da2b28SAriff Abdullah 
850987e5972SCameron Grant 	return codec;
851987e5972SCameron Grant }
852987e5972SCameron Grant 
85333dbf14aSCameron Grant void
85433dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec)
85533dbf14aSCameron Grant {
85666ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
8570f55ac6cSCameron Grant 	if (codec->methods != NULL)
8580f55ac6cSCameron Grant 		kobj_delete(codec->methods, M_AC97);
85966ef8af5SCameron Grant 	snd_mtxfree(codec->lock);
8600f55ac6cSCameron Grant 	free(codec, M_AC97);
86133dbf14aSCameron Grant }
86233dbf14aSCameron Grant 
86379bb7d52SCameron Grant void
86479bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val)
86579bb7d52SCameron Grant {
86679bb7d52SCameron Grant 	codec->flags = val;
86779bb7d52SCameron Grant }
86879bb7d52SCameron Grant 
86979bb7d52SCameron Grant u_int32_t
87079bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec)
87179bb7d52SCameron Grant {
87279bb7d52SCameron Grant 	return codec->flags;
87379bb7d52SCameron Grant }
87479bb7d52SCameron Grant 
8750f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
8760f55ac6cSCameron Grant 
877a580b31aSAriff Abdullah static int
878a580b31aSAriff Abdullah sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS)
879a580b31aSAriff Abdullah {
880a580b31aSAriff Abdullah 	struct ac97_info *codec;
881a580b31aSAriff Abdullah 	int ea, inv, err = 0;
882a580b31aSAriff Abdullah 	u_int16_t val;
883a580b31aSAriff Abdullah 
884a580b31aSAriff Abdullah 	codec = oidp->oid_arg1;
885a580b31aSAriff Abdullah 	if (codec == NULL || codec->id == 0 || codec->lock == NULL)
886a580b31aSAriff Abdullah 		return EINVAL;
887a580b31aSAriff Abdullah 	snd_mtxlock(codec->lock);
888a580b31aSAriff Abdullah 	val = ac97_rdcd(codec, AC97_REG_POWER);
889a580b31aSAriff Abdullah 	inv = (codec->flags & AC97_F_EAPD_INV) ? 0 : 1;
890a580b31aSAriff Abdullah 	ea = (val >> 15) ^ inv;
891a580b31aSAriff Abdullah 	snd_mtxunlock(codec->lock);
892041b706bSDavid Malone 	err = sysctl_handle_int(oidp, &ea, 0, req);
893a580b31aSAriff Abdullah 	if (err == 0 && req->newptr != NULL) {
894a580b31aSAriff Abdullah 		if (ea != 0 && ea != 1)
895a580b31aSAriff Abdullah 			return EINVAL;
896a580b31aSAriff Abdullah 		if (ea != ((val >> 15) ^ inv)) {
897a580b31aSAriff Abdullah 			snd_mtxlock(codec->lock);
898a580b31aSAriff Abdullah 			ac97_wrcd(codec, AC97_REG_POWER, val ^ 0x8000);
899a580b31aSAriff Abdullah 			snd_mtxunlock(codec->lock);
900a580b31aSAriff Abdullah 		}
901a580b31aSAriff Abdullah 	}
902a580b31aSAriff Abdullah 	return err;
903a580b31aSAriff Abdullah }
904a580b31aSAriff Abdullah 
905a580b31aSAriff Abdullah static void
906a580b31aSAriff Abdullah ac97_init_sysctl(struct ac97_info *codec)
907a580b31aSAriff Abdullah {
908a580b31aSAriff Abdullah 	u_int16_t orig, val;
909a580b31aSAriff Abdullah 
910a580b31aSAriff Abdullah 	if (codec == NULL || codec->dev == NULL)
911a580b31aSAriff Abdullah 		return;
912a580b31aSAriff Abdullah 	snd_mtxlock(codec->lock);
913a580b31aSAriff Abdullah 	orig = ac97_rdcd(codec, AC97_REG_POWER);
914a580b31aSAriff Abdullah 	ac97_wrcd(codec, AC97_REG_POWER, orig ^ 0x8000);
915a580b31aSAriff Abdullah 	val = ac97_rdcd(codec, AC97_REG_POWER);
916a580b31aSAriff Abdullah 	ac97_wrcd(codec, AC97_REG_POWER, orig);
917a580b31aSAriff Abdullah 	snd_mtxunlock(codec->lock);
918a580b31aSAriff Abdullah 	if ((val & 0x8000) == (orig & 0x8000))
919a580b31aSAriff Abdullah 		return;
920a580b31aSAriff Abdullah 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(codec->dev),
921a580b31aSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(codec->dev)),
922*7029da5cSPawel Biernacki             OID_AUTO, "eapd",
923*7029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
924a580b31aSAriff Abdullah 	    codec, sizeof(codec), sysctl_hw_snd_ac97_eapd,
925a580b31aSAriff Abdullah 	    "I", "AC97 External Amplifier");
926a580b31aSAriff Abdullah }
927a580b31aSAriff Abdullah 
928987e5972SCameron Grant static int
92966ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m)
930987e5972SCameron Grant {
931987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
932341f16ccSCameron Grant 	u_int32_t i, mask;
933341f16ccSCameron Grant 
93439004e69SCameron Grant 	if (codec == NULL)
93539004e69SCameron Grant 		return -1;
936341f16ccSCameron Grant 
937e620d959SCameron Grant 	if (ac97_initmixer(codec))
938e620d959SCameron Grant 		return -1;
939341f16ccSCameron Grant 
9407699548fSAriff Abdullah 	switch (codec->id) {
9417699548fSAriff Abdullah 	case 0x41445374:	/* AD1981B */
94255431491SAriff Abdullah 		switch (codec->subvendor) {
94355431491SAriff Abdullah 		case 0x02d91014:
944fd7390d6SAriff Abdullah 			/*
945fd7390d6SAriff Abdullah 			 * IBM Thinkcentre:
94655431491SAriff Abdullah 			 *
94755431491SAriff Abdullah 			 * Tie "ogain" and "phout" to "vol" since its
948fd7390d6SAriff Abdullah 			 * master volume is basically useless and can't
949fd7390d6SAriff Abdullah 			 * control anything.
950fd7390d6SAriff Abdullah 			 */
9517699548fSAriff Abdullah 			mask = 0;
9527699548fSAriff Abdullah 			if (codec->mix[SOUND_MIXER_OGAIN].enable)
9537699548fSAriff Abdullah 				mask |= SOUND_MASK_OGAIN;
9547699548fSAriff Abdullah 			if (codec->mix[SOUND_MIXER_PHONEOUT].enable)
9557699548fSAriff Abdullah 				mask |= SOUND_MASK_PHONEOUT;
9567699548fSAriff Abdullah 			if (codec->mix[SOUND_MIXER_VOLUME].enable)
957a580b31aSAriff Abdullah 				mix_setparentchild(m, SOUND_MIXER_VOLUME,
958a580b31aSAriff Abdullah 				    mask);
9597699548fSAriff Abdullah 			else {
960a580b31aSAriff Abdullah 				mix_setparentchild(m, SOUND_MIXER_VOLUME,
961a580b31aSAriff Abdullah 				    mask);
962a580b31aSAriff Abdullah 				mix_setrealdev(m, SOUND_MIXER_VOLUME,
963a580b31aSAriff Abdullah 				    SOUND_MIXER_NONE);
9647699548fSAriff Abdullah 			}
96555431491SAriff Abdullah 			break;
96655431491SAriff Abdullah 		case 0x099c103c:
96755431491SAriff Abdullah 			/*
96855431491SAriff Abdullah 			 * HP nx6110:
96955431491SAriff Abdullah 			 *
97055431491SAriff Abdullah 			 * By default, "vol" is controlling internal speakers
97155431491SAriff Abdullah 			 * (not a master volume!) and "ogain" is controlling
97255431491SAriff Abdullah 			 * headphone. Enable dummy "phout" so it can be
97355431491SAriff Abdullah 			 * remapped to internal speakers and virtualize
97455431491SAriff Abdullah 			 * "vol" to control both.
97555431491SAriff Abdullah 			 */
97655431491SAriff Abdullah 			codec->mix[SOUND_MIXER_OGAIN].enable = 1;
97755431491SAriff Abdullah 			codec->mix[SOUND_MIXER_PHONEOUT].enable = 1;
97855431491SAriff Abdullah 			mix_setrealdev(m, SOUND_MIXER_PHONEOUT,
97955431491SAriff Abdullah 			    SOUND_MIXER_VOLUME);
98055431491SAriff Abdullah 			mix_setrealdev(m, SOUND_MIXER_VOLUME,
98155431491SAriff Abdullah 			    SOUND_MIXER_NONE);
98255431491SAriff Abdullah 			mix_setparentchild(m, SOUND_MIXER_VOLUME,
98355431491SAriff Abdullah 			    SOUND_MASK_OGAIN | SOUND_MASK_PHONEOUT);
98455431491SAriff Abdullah 			break;
98555431491SAriff Abdullah 		default:
98655431491SAriff Abdullah 			break;
987a580b31aSAriff Abdullah 		}
9887699548fSAriff Abdullah 		break;
9897699548fSAriff Abdullah 	case 0x434d4941:	/* CMI9738 */
9907699548fSAriff Abdullah 	case 0x434d4961:	/* CMI9739 */
9917699548fSAriff Abdullah 	case 0x434d4978:	/* CMI9761 */
9927699548fSAriff Abdullah 	case 0x434d4982:	/* CMI9761 */
9937699548fSAriff Abdullah 	case 0x434d4983:	/* CMI9761 */
9947699548fSAriff Abdullah 		bzero(&codec->mix[SOUND_MIXER_PCM],
9957699548fSAriff Abdullah 		    sizeof(codec->mix[SOUND_MIXER_PCM]));
996e510f521SAriff Abdullah 		pcm_setflags(codec->dev, pcm_getflags(codec->dev) |
997e510f521SAriff Abdullah 		    SD_F_SOFTPCMVOL);
9987699548fSAriff Abdullah 		/* XXX How about master volume ? */
9997699548fSAriff Abdullah 		break;
10007699548fSAriff Abdullah 	default:
10017699548fSAriff Abdullah 		break;
10027699548fSAriff Abdullah 	}
10037699548fSAriff Abdullah 
100490da2b28SAriff Abdullah 	if (pcm_getflags(codec->dev) & SD_F_SOFTPCMVOL)
100590da2b28SAriff Abdullah 		ac97_wrcd(codec, AC97_MIX_PCM, 0);
10067699548fSAriff Abdullah #if 0
10077699548fSAriff Abdullah 	/* XXX For the sake of debugging purposes */
10087699548fSAriff Abdullah 	mix_setparentchild(m, SOUND_MIXER_VOLUME,
10097699548fSAriff Abdullah 	    SOUND_MASK_PCM | SOUND_MASK_CD);
10107699548fSAriff Abdullah 	mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
10117699548fSAriff Abdullah 	ac97_wrcd(codec, AC97_MIX_MASTER, 0);
10127699548fSAriff Abdullah #endif
10137699548fSAriff Abdullah 
1014341f16ccSCameron Grant 	mask = 0;
101569f6d261SAriff Abdullah 	for (i = 0; i < AC97_MIXER_SIZE; i++)
1016341f16ccSCameron Grant 		mask |= codec->mix[i].enable? 1 << i : 0;
1017341f16ccSCameron Grant 	mix_setdevs(m, mask);
1018341f16ccSCameron Grant 
1019341f16ccSCameron Grant 	mask = 0;
102069f6d261SAriff Abdullah 	for (i = 0; i < AC97_MIXER_SIZE; i++)
1021341f16ccSCameron Grant 		mask |= codec->mix[i].recidx? 1 << i : 0;
1022341f16ccSCameron Grant 	mix_setrecdevs(m, mask);
1023a580b31aSAriff Abdullah 
1024a580b31aSAriff Abdullah 	ac97_init_sysctl(codec);
1025a580b31aSAriff Abdullah 
1026987e5972SCameron Grant 	return 0;
1027987e5972SCameron Grant }
1028987e5972SCameron Grant 
1029987e5972SCameron Grant static int
103066ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m)
103133dbf14aSCameron Grant {
103233dbf14aSCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
1033341f16ccSCameron Grant 
103433dbf14aSCameron Grant 	if (codec == NULL)
103533dbf14aSCameron Grant 		return -1;
103633dbf14aSCameron Grant 	/*
103733dbf14aSCameron Grant 	if (ac97_uninitmixer(codec))
103833dbf14aSCameron Grant 		return -1;
103933dbf14aSCameron Grant 	*/
104033dbf14aSCameron Grant 	ac97_destroy(codec);
104133dbf14aSCameron Grant 	return 0;
104233dbf14aSCameron Grant }
104333dbf14aSCameron Grant 
104433dbf14aSCameron Grant static int
104566ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m)
10469ec437a3SCameron Grant {
10479ec437a3SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
10489ec437a3SCameron Grant 
10499ec437a3SCameron Grant 	if (codec == NULL)
10509ec437a3SCameron Grant 		return -1;
10519ec437a3SCameron Grant 	return ac97_reinitmixer(codec);
10529ec437a3SCameron Grant }
10539ec437a3SCameron Grant 
10549ec437a3SCameron Grant static int
105566ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
1056987e5972SCameron Grant {
1057987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
1058341f16ccSCameron Grant 
105969f6d261SAriff Abdullah 	if (codec == NULL || dev >= AC97_MIXER_SIZE)
106039004e69SCameron Grant 		return -1;
1061987e5972SCameron Grant 	return ac97_setmixer(codec, dev, left, right);
1062987e5972SCameron Grant }
1063987e5972SCameron Grant 
106490da2b28SAriff Abdullah static u_int32_t
106566ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
1066987e5972SCameron Grant {
1067987e5972SCameron Grant 	int i;
1068987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
1069341f16ccSCameron Grant 
107039004e69SCameron Grant 	if (codec == NULL)
107139004e69SCameron Grant 		return -1;
107269f6d261SAriff Abdullah 	for (i = 0; i < AC97_MIXER_SIZE; i++)
107339004e69SCameron Grant 		if ((src & (1 << i)) != 0)
107439004e69SCameron Grant 			break;
107590da2b28SAriff Abdullah 	return (ac97_setrecsrc(codec, i) == 0)? 1U << i : 0xffffffffU;
1076987e5972SCameron Grant }
1077987e5972SCameron Grant 
10780f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = {
10790f55ac6cSCameron Grant     	KOBJMETHOD(mixer_init,		ac97mix_init),
10800f55ac6cSCameron Grant     	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
10810f55ac6cSCameron Grant     	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
10820f55ac6cSCameron Grant     	KOBJMETHOD(mixer_set,		ac97mix_set),
10830f55ac6cSCameron Grant     	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
108490da2b28SAriff Abdullah 	KOBJMETHOD_END
1085987e5972SCameron Grant };
10860f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer);
10870f55ac6cSCameron Grant 
10880f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
10890f55ac6cSCameron Grant 
10900f55ac6cSCameron Grant kobj_class_t
10910f55ac6cSCameron Grant ac97_getmixerclass(void)
10920f55ac6cSCameron Grant {
10930f55ac6cSCameron Grant 	return &ac97mixer_class;
10940f55ac6cSCameron Grant }
1095