1139749Simp/*-
2119853Scg * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
350724Scg * All rights reserved.
450724Scg *
550724Scg * Redistribution and use in source and binary forms, with or without
650724Scg * modification, are permitted provided that the following conditions
750724Scg * are met:
850724Scg * 1. Redistributions of source code must retain the above copyright
950724Scg *    notice, this list of conditions and the following disclaimer.
1050724Scg * 2. Redistributions in binary form must reproduce the above copyright
1150724Scg *    notice, this list of conditions and the following disclaimer in the
1250724Scg *    documentation and/or other materials provided with the distribution.
1350724Scg *
1450724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1550724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1650724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1750724Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1850724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1950724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2050724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2150724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2250724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2350724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2450724Scg * SUCH DAMAGE.
2550724Scg */
2650724Scg
27193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS
28193640Sariff#include "opt_snd.h"
29193640Sariff#endif
30193640Sariff
3153465Scg#include <dev/sound/pcm/sound.h>
3253465Scg#include <dev/sound/pcm/ac97.h>
33109818Sorion#include <dev/sound/pcm/ac97_patch.h>
3450724Scg
35164614Sariff#include <dev/pci/pcivar.h>
36164614Sariff
3770134Scg#include "mixer_if.h"
3850724Scg
3982180ScgSND_DECLARE_FILE("$FreeBSD: releng/10.2/sys/dev/sound/pcm/ac97.c 227293 2011-11-07 06:44:47Z ed $");
4082180Scg
41227293Sedstatic MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
4250724Scg
4378668Scgstruct ac97mixtable_entry {
44170060Sariff	int reg;		/* register index		*/
45113783Sorion				/* reg < 0 if inverted polarity	*/
46113783Sorion	unsigned bits:4;	/* width of control field	*/
47113783Sorion	unsigned ofs:4;		/* offset (only if stereo=0)	*/
48113783Sorion	unsigned stereo:1;	/* set for stereo controls	*/
49113783Sorion	unsigned mute:1;	/* bit15 is MUTE		*/
50113783Sorion	unsigned recidx:4;	/* index in rec mux		*/
51113783Sorion	unsigned mask:1;	/* use only masked bits		*/
52113783Sorion	unsigned enable:1;	/* entry is enabled		*/
5378668Scg};
5478668Scg
55170342Sariff#define AC97_MIXER_SIZE		SOUND_MIXER_NRDEVICES
56170342Sariff
5774763Scgstruct ac97_info {
5874763Scg	kobj_t methods;
5974763Scg	device_t dev;
6074763Scg	void *devinfo;
61111679Sorion	u_int32_t id;
62168861Sariff	u_int32_t subvendor;
6374763Scg	unsigned count, caps, se, extcaps, extid, extstat, noext:1;
6478668Scg	u_int32_t flags;
65170342Sariff	struct ac97mixtable_entry mix[AC97_MIXER_SIZE];
66170342Sariff	char name[16];
67107285Scg	struct mtx *lock;
6874763Scg};
6974763Scg
70111679Sorionstruct ac97_vendorid {
71111679Sorion	u_int32_t   id;
72111679Sorion	const char *name;
73111679Sorion};
74111679Sorion
7550724Scgstruct ac97_codecid {
76111679Sorion	u_int32_t  id;
77111679Sorion	u_int8_t   stepmask;
78111679Sorion	u_int8_t   noext:1;
79111679Sorion	char 	  *name;
80109818Sorion	ac97_patch patch;
8150724Scg};
8250724Scg
83170342Sariffstatic const struct ac97mixtable_entry ac97mixtable_default[AC97_MIXER_SIZE] = {
84113783Sorion    /*	[offset]			reg	     bits of st mu re mk en */
8566013Scg	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0, 1 },
86119209Sorion	[SOUND_MIXER_OGAIN]	= { AC97_MIX_AUXOUT, 	5, 0, 1, 1, 0, 0, 0 },
8766013Scg	[SOUND_MIXER_PHONEOUT]	= { AC97_MIX_MONO, 	5, 0, 0, 1, 7, 0, 0 },
8866013Scg	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1, 0 },
8966013Scg	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1, 0 },
9066013Scg	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0, 1 },
9166013Scg	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0, 0 },
9266013Scg	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0, 1 },
9366013Scg	[SOUND_MIXER_PHONEIN]	= { AC97_MIX_PHONE, 	5, 0, 0, 1, 8, 0, 0 },
94113783Sorion	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 1, 1 },
95113783Sorion	/* use igain for the mic 20dB boost */
96113783Sorion	[SOUND_MIXER_IGAIN] 	= { -AC97_MIX_MIC, 	1, 6, 0, 0, 0, 1, 1 },
9766013Scg	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
9866013Scg	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
9966013Scg	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
10066013Scg	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
10150724Scg};
10250724Scg
103111679Sorionstatic const struct ac97_vendorid ac97vendorid[] = {
104111679Sorion	{ 0x41445300, "Analog Devices" },
105111679Sorion	{ 0x414b4d00, "Asahi Kasei" },
106111679Sorion	{ 0x414c4300, "Realtek" },
107111679Sorion	{ 0x414c4700, "Avance Logic" },
108111679Sorion	{ 0x43525900, "Cirrus Logic" },
109111679Sorion	{ 0x434d4900, "C-Media Electronics" },
110111679Sorion	{ 0x43585400, "Conexant" },
111124875Smatk	{ 0x44543000, "Diamond Technology" },
112113788Sorion	{ 0x454d4300, "eMicro" },
113111679Sorion	{ 0x45838300, "ESS Technology" },
114124875Smatk	{ 0x48525300, "Intersil" },
115111679Sorion	{ 0x49434500, "ICEnsemble" },
116124875Smatk	{ 0x49544500, "ITE, Inc." },
117111679Sorion	{ 0x4e534300, "National Semiconductor" },
118111679Sorion	{ 0x50534300, "Philips Semiconductor" },
119111679Sorion	{ 0x83847600, "SigmaTel" },
120124875Smatk	{ 0x53494c00, "Silicon Laboratories" },
121111679Sorion	{ 0x54524100, "TriTech" },
122124875Smatk	{ 0x54584e00, "Texas Instruments" },
123111679Sorion	{ 0x56494100, "VIA Technologies" },
124124875Smatk	{ 0x57454300, "Winbond" },
125111679Sorion	{ 0x574d4c00, "Wolfson" },
126111679Sorion	{ 0x594d4800, "Yamaha" },
127149949Snetchild	/*
128149949Snetchild	 * XXX This is a fluke, really! The real vendor
129149949Snetchild	 * should be SigmaTel, not this! This should be
130149949Snetchild	 * removed someday!
131149949Snetchild	 */
132119547Sorion	{ 0x01408300, "Creative" },
133111679Sorion	{ 0x00000000, NULL }
134111679Sorion};
135111679Sorion
13650724Scgstatic struct ac97_codecid ac97codecid[] = {
137111679Sorion	{ 0x41445303, 0x00, 0, "AD1819",	0 },
138111679Sorion	{ 0x41445340, 0x00, 0, "AD1881",	0 },
139111679Sorion	{ 0x41445348, 0x00, 0, "AD1881A",	0 },
140111679Sorion	{ 0x41445360, 0x00, 0, "AD1885",	0 },
141111679Sorion	{ 0x41445361, 0x00, 0, "AD1886", 	ad1886_patch },
142113057Sorion	{ 0x41445362, 0x00, 0, "AD1887", 	0 },
143113057Sorion	{ 0x41445363, 0x00, 0, "AD1886A", 	0 },
144144865Sscottl	{ 0x41445368, 0x00, 0, "AD1888", 	ad198x_patch },
145119209Sorion	{ 0x41445370, 0x00, 0, "AD1980",	ad198x_patch },
146113057Sorion	{ 0x41445372, 0x00, 0, "AD1981A",	0 },
147162738Sariff	{ 0x41445374, 0x00, 0, "AD1981B",	ad1981b_patch },
148119209Sorion	{ 0x41445375, 0x00, 0, "AD1985",	ad198x_patch },
149154094Sariff	{ 0x41445378, 0x00, 0, "AD1986",	ad198x_patch },
150111679Sorion	{ 0x414b4d00, 0x00, 1, "AK4540", 	0 },
151111679Sorion	{ 0x414b4d01, 0x00, 1, "AK4542", 	0 },
152111679Sorion	{ 0x414b4d02, 0x00, 1, "AK4543", 	0 },
153124875Smatk	{ 0x414b4d06, 0x00, 0, "AK4544A",	0 },
154124875Smatk	{ 0x454b4d07, 0x00, 0, "AK4545",	0 },
155111679Sorion	{ 0x414c4320, 0x0f, 0, "ALC100",	0 },
156119209Sorion	{ 0x414c4730, 0x0f, 0, "ALC101",	0 },
157111679Sorion	{ 0x414c4710, 0x0f, 0, "ALC200", 	0 },
158111679Sorion	{ 0x414c4740, 0x0f, 0, "ALC202", 	0 },
159111679Sorion	{ 0x414c4720, 0x0f, 0, "ALC650", 	0 },
160152815Sariff	{ 0x414c4752, 0x0f, 0, "ALC250",	0 },
161168861Sariff	{ 0x414c4760, 0x0f, 0, "ALC655",	alc655_patch },
162152939Syongari	{ 0x414c4770, 0x0f, 0, "ALC203",	0 },
163122515Skuriyama	{ 0x414c4780, 0x0f, 0, "ALC658",	0 },
164124875Smatk	{ 0x414c4790, 0x0f, 0, "ALC850",	0 },
165111679Sorion	{ 0x43525900, 0x07, 0, "CS4297", 	0 },
166111679Sorion	{ 0x43525910, 0x07, 0, "CS4297A", 	0 },
167111679Sorion	{ 0x43525920, 0x07, 0, "CS4294/98",	0 },
168129044Ssanpei	{ 0x4352592d, 0x07, 0, "CS4294",	0 },
169111679Sorion	{ 0x43525930, 0x07, 0, "CS4299",	0 },
170111679Sorion	{ 0x43525940, 0x07, 0, "CS4201",	0 },
171112150Sorion	{ 0x43525958, 0x07, 0, "CS4205",	0 },
172111679Sorion	{ 0x43525960, 0x07, 0, "CS4291A",	0 },
173150825Snetchild	{ 0x434d4961, 0x00, 0, "CMI9739",	cmi9739_patch },
174111679Sorion	{ 0x434d4941, 0x00, 0, "CMI9738",	0 },
175150825Snetchild	{ 0x434d4978, 0x00, 0, "CMI9761",	0 },
176150825Snetchild	{ 0x434d4982, 0x00, 0, "CMI9761",	0 },
177150825Snetchild	{ 0x434d4983, 0x00, 0, "CMI9761",	0 },
178124875Smatk	{ 0x43585421, 0x00, 0, "HSD11246",	0 },
179124875Smatk	{ 0x43585428, 0x07, 0, "CX20468",	0 },
180152422Sariff	{ 0x43585430, 0x00, 0, "CX20468-21",	0 },
181124875Smatk	{ 0x44543000, 0x00, 0, "DT0398",	0 },
182113788Sorion	{ 0x454d4323, 0x00, 0, "EM28023",	0 },
183113788Sorion	{ 0x454d4328, 0x00, 0, "EM28028",	0 },
184111679Sorion	{ 0x45838308, 0x00, 0, "ES1988",	0 }, /* Formerly ES1921(?) */
185124875Smatk	{ 0x48525300, 0x00, 0, "HMP9701",	0 },
186111679Sorion	{ 0x49434501, 0x00, 0, "ICE1230",	0 },
187111679Sorion	{ 0x49434511, 0x00, 0, "ICE1232",	0 },
188111679Sorion	{ 0x49434514, 0x00, 0, "ICE1232A",	0 },
189119250Sorion	{ 0x49434551, 0x03, 0, "VT1616",	0 }, /* Via badged ICE */
190124875Smatk	{ 0x49544520, 0x00, 0, "ITE2226E",	0 },
191124875Smatk	{ 0x49544560, 0x07, 0, "ITE2646E",	0 }, /* XXX: patch needed */
192111679Sorion	{ 0x4e534340, 0x00, 0, "LM4540",	0 }, /* Spec blank on revid */
193111679Sorion	{ 0x4e534343, 0x00, 0, "LM4543",	0 }, /* Ditto */
194111679Sorion	{ 0x4e534346, 0x00, 0, "LM4546A",	0 },
195111679Sorion	{ 0x4e534348, 0x00, 0, "LM4548A",	0 },
196124875Smatk	{ 0x4e534331, 0x00, 0, "LM4549",	0 },
197111679Sorion	{ 0x4e534349, 0x00, 0, "LM4549A",	0 },
198111679Sorion	{ 0x4e534350, 0x00, 0, "LM4550",	0 },
199111679Sorion	{ 0x50534301, 0x00, 0, "UCB1510",	0 },
200111679Sorion	{ 0x50534304, 0x00, 0, "UCB1400",	0 },
201111679Sorion	{ 0x83847600, 0x00, 0, "STAC9700/83/84",	0 },
202111679Sorion	{ 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
203113788Sorion	{ 0x83847605, 0x00, 0, "STAC9704",	0 },
204111679Sorion	{ 0x83847608, 0x00, 0, "STAC9708/11",	0 },
205111679Sorion	{ 0x83847609, 0x00, 0, "STAC9721/23",	0 },
206111679Sorion	{ 0x83847644, 0x00, 0, "STAC9744/45",	0 },
207111679Sorion	{ 0x83847650, 0x00, 0, "STAC9750/51",	0 },
208111679Sorion	{ 0x83847652, 0x00, 0, "STAC9752/53",	0 },
209111679Sorion	{ 0x83847656, 0x00, 0, "STAC9756/57",	0 },
210111679Sorion	{ 0x83847658, 0x00, 0, "STAC9758/59",	0 },
211111679Sorion	{ 0x83847660, 0x00, 0, "STAC9760/61",	0 }, /* Extrapolated */
212111679Sorion	{ 0x83847662, 0x00, 0, "STAC9762/63",	0 }, /* Extrapolated */
213150825Snetchild	{ 0x83847666, 0x00, 0, "STAC9766/67",	0 },
214111679Sorion	{ 0x53494c22, 0x00, 0, "Si3036",	0 },
215111679Sorion	{ 0x53494c23, 0x00, 0, "Si3038",	0 },
216111679Sorion	{ 0x54524103, 0x00, 0, "TR28023",	0 }, /* Extrapolated */
217111679Sorion	{ 0x54524106, 0x00, 0, "TR28026",	0 },
218111679Sorion	{ 0x54524108, 0x00, 0, "TR28028",	0 },
219111679Sorion	{ 0x54524123, 0x00, 0, "TR28602",	0 },
220124875Smatk	{ 0x54524e03, 0x07, 0, "TLV320AIC27",	0 },
221124875Smatk	{ 0x54584e20, 0x00, 0, "TLC320AD90",	0 },
222111679Sorion	{ 0x56494161, 0x00, 0, "VIA1612A",      0 },
223152422Sariff	{ 0x56494170, 0x00, 0, "VIA1617A",      0 },
224111679Sorion	{ 0x574d4c00, 0x00, 0, "WM9701A",	0 },
225111679Sorion	{ 0x574d4c03, 0x00, 0, "WM9703/4/7/8",	0 },
226111679Sorion	{ 0x574d4c04, 0x00, 0, "WM9704Q",	0 },
227111679Sorion	{ 0x574d4c05, 0x00, 0, "WM9705/10",	0 },
228124875Smatk	{ 0x574d4d09, 0x00, 0, "WM9709",	0 },
229124875Smatk	{ 0x574d4c12, 0x00, 0, "WM9711/12",	0 }, /* XXX: patch needed */
230124875Smatk	{ 0x57454301, 0x00, 0, "W83971D",	0 },
231111679Sorion	{ 0x594d4800, 0x00, 0, "YMF743",	0 },
232111679Sorion	{ 0x594d4802, 0x00, 0, "YMF752",	0 },
233111679Sorion	{ 0x594d4803, 0x00, 0, "YMF753",	0 },
234149949Snetchild	/*
235149949Snetchild	 * XXX This is a fluke, really! The real codec
236149949Snetchild	 * should be STAC9704, not this! This should be
237149949Snetchild	 * removed someday!
238149949Snetchild	 */
239119547Sorion	{ 0x01408384, 0x00, 0, "EV1938",	0 },
240111679Sorion	{ 0, 0, 0, NULL, 0 }
24150724Scg};
24250724Scg
24350724Scgstatic char *ac97enhancement[] = {
24460960Scg	"no 3D Stereo Enhancement",
24550724Scg	"Analog Devices Phat Stereo",
24650724Scg	"Creative Stereo Enhancement",
24750724Scg	"National Semi 3D Stereo Enhancement",
24850724Scg	"Yamaha Ymersion",
24950724Scg	"BBE 3D Stereo Enhancement",
25050724Scg	"Crystal Semi 3D Stereo Enhancement",
25150724Scg	"Qsound QXpander",
25250724Scg	"Spatializer 3D Stereo Enhancement",
25350724Scg	"SRS 3D Stereo Enhancement",
25450724Scg	"Platform Tech 3D Stereo Enhancement",
25550724Scg	"AKM 3D Audio",
25650724Scg	"Aureal Stereo Enhancement",
25750724Scg	"Aztech 3D Enhancement",
25850724Scg	"Binaura 3D Audio Enhancement",
25950724Scg	"ESS Technology Stereo Enhancement",
26050724Scg	"Harman International VMAx",
26150724Scg	"Nvidea 3D Stereo Enhancement",
26250724Scg	"Philips Incredible Sound",
26350724Scg	"Texas Instruments 3D Stereo Enhancement",
26450724Scg	"VLSI Technology 3D Stereo Enhancement",
26550724Scg	"TriTech 3D Stereo Enhancement",
26650724Scg	"Realtek 3D Stereo Enhancement",
26750724Scg	"Samsung 3D Stereo Enhancement",
26850724Scg	"Wolfson Microelectronics 3D Enhancement",
26950724Scg	"Delta Integration 3D Enhancement",
27050724Scg	"SigmaTel 3D Enhancement",
27150724Scg	"Reserved 27",
27250724Scg	"Rockwell 3D Stereo Enhancement",
27350724Scg	"Reserved 29",
27450724Scg	"Reserved 30",
27550724Scg	"Reserved 31"
27650724Scg};
27750724Scg
27850724Scgstatic char *ac97feature[] = {
27950724Scg	"mic channel",
28050724Scg	"reserved",
28150724Scg	"tone",
28250724Scg	"simulated stereo",
28350724Scg	"headphone",
28450724Scg	"bass boost",
28550724Scg	"18 bit DAC",
28650724Scg	"20 bit DAC",
28750724Scg	"18 bit ADC",
28850724Scg	"20 bit ADC"
28950724Scg};
29050724Scg
29158384Scgstatic char *ac97extfeature[] = {
29258384Scg	"variable rate PCM",
29358384Scg	"double rate PCM",
29458384Scg	"reserved 1",
29558384Scg	"variable rate mic",
29658384Scg	"reserved 2",
29758384Scg	"reserved 3",
29858384Scg	"center DAC",
29958384Scg	"surround DAC",
30058384Scg	"LFE DAC",
30158384Scg	"AMAP",
30258384Scg	"reserved 4",
30358384Scg	"reserved 5",
30458384Scg	"reserved 6",
30558384Scg	"reserved 7",
30658384Scg};
30758384Scg
308109818Sorionu_int16_t
309109818Sorionac97_rdcd(struct ac97_info *codec, int reg)
31058384Scg{
311149949Snetchild	if (codec->flags & AC97_F_RDCD_BUG) {
312149949Snetchild		u_int16_t i[2], j = 100;
313149949Snetchild
314149949Snetchild		i[0] = AC97_READ(codec->methods, codec->devinfo, reg);
315149949Snetchild		i[1] = AC97_READ(codec->methods, codec->devinfo, reg);
316149949Snetchild		while (i[0] != i[1] && j)
317149949Snetchild			i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg);
318149949Snetchild#if 0
319149949Snetchild		if (j < 100) {
320149949Snetchild			device_printf(codec->dev, "%s(): Inconsistent register value at"
321149949Snetchild					" 0x%08x (retry: %d)\n", __func__, reg, 100 - j);
322149949Snetchild		}
323149949Snetchild#endif
324149949Snetchild		return i[!(j & 1)];
325149949Snetchild	}
32670134Scg	return AC97_READ(codec->methods, codec->devinfo, reg);
32758384Scg}
32858384Scg
329109818Sorionvoid
330109818Sorionac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
33158384Scg{
33270134Scg	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
33358384Scg}
33458384Scg
33595499Sorionstatic void
33695499Sorionac97_reset(struct ac97_info *codec)
33795499Sorion{
33895499Sorion	u_int32_t i, ps;
339109818Sorion	ac97_wrcd(codec, AC97_REG_RESET, 0);
34095499Sorion	for (i = 0; i < 500; i++) {
341109818Sorion		ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
342107285Scg		if (ps == AC97_POWER_STATUS)
34395499Sorion			return;
34495499Sorion		DELAY(1000);
34595499Sorion	}
346110258Sorion	device_printf(codec->dev, "AC97 reset timed out.\n");
34795499Sorion}
34895499Sorion
34958384Scgint
35058384Scgac97_setrate(struct ac97_info *codec, int which, int rate)
35158384Scg{
35258384Scg	u_int16_t v;
35358384Scg
35458384Scg	switch(which) {
355107285Scg	case AC97_REGEXT_FDACRATE:
35658384Scg	case AC97_REGEXT_SDACRATE:
35758384Scg	case AC97_REGEXT_LDACRATE:
358107285Scg	case AC97_REGEXT_LADCRATE:
35958384Scg	case AC97_REGEXT_MADCRATE:
36058384Scg		break;
36158384Scg
36258384Scg	default:
36358384Scg		return -1;
36458384Scg	}
36558384Scg
36674763Scg	snd_mtxlock(codec->lock);
36758384Scg	if (rate != 0) {
36858384Scg		v = rate;
36958384Scg		if (codec->extstat & AC97_EXTCAP_DRA)
37058384Scg			v >>= 1;
371109818Sorion		ac97_wrcd(codec, which, v);
37258384Scg	}
373109818Sorion	v = ac97_rdcd(codec, which);
37458384Scg	if (codec->extstat & AC97_EXTCAP_DRA)
37558384Scg		v <<= 1;
37674763Scg	snd_mtxunlock(codec->lock);
37758384Scg	return v;
37858384Scg}
37958384Scg
38058384Scgint
38158384Scgac97_setextmode(struct ac97_info *codec, u_int16_t mode)
38258384Scg{
38358384Scg	mode &= AC97_EXTCAPS;
38486707Sorion	if ((mode & ~codec->extcaps) != 0) {
385107285Scg		device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
38686707Sorion			      mode);
38758384Scg		return -1;
38886707Sorion	}
38974763Scg	snd_mtxlock(codec->lock);
390109818Sorion	ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
391109818Sorion	codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
39274763Scg	snd_mtxunlock(codec->lock);
39358384Scg	return (mode == codec->extstat)? 0 : -1;
39458384Scg}
39558384Scg
39667652Scgu_int16_t
39767652Scgac97_getextmode(struct ac97_info *codec)
39867652Scg{
39967652Scg	return codec->extstat;
40067652Scg}
40167652Scg
40267652Scgu_int16_t
40367652Scgac97_getextcaps(struct ac97_info *codec)
40467652Scg{
40567652Scg	return codec->extcaps;
40667652Scg}
40767652Scg
40883612Scgu_int16_t
40983612Scgac97_getcaps(struct ac97_info *codec)
41083612Scg{
41183612Scg	return codec->caps;
41283612Scg}
41383612Scg
414168861Sariffu_int32_t
415168861Sariffac97_getsubvendor(struct ac97_info *codec)
416168861Sariff{
417168861Sariff	return codec->subvendor;
418168861Sariff}
419168861Sariff
42050724Scgstatic int
42150724Scgac97_setrecsrc(struct ac97_info *codec, int channel)
42250724Scg{
42350724Scg	struct ac97mixtable_entry *e = &codec->mix[channel];
42466013Scg
42550724Scg	if (e->recidx > 0) {
42650724Scg		int val = e->recidx - 1;
42750724Scg		val |= val << 8;
42874763Scg		snd_mtxlock(codec->lock);
429109818Sorion		ac97_wrcd(codec, AC97_REG_RECSEL, val);
43074763Scg		snd_mtxunlock(codec->lock);
43150724Scg		return 0;
43258384Scg	} else
43358384Scg		return -1;
43450724Scg}
43550724Scg
43650724Scgstatic int
43750724Scgac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
43850724Scg{
43950724Scg	struct ac97mixtable_entry *e = &codec->mix[channel];
44066013Scg
44166013Scg	if (e->reg && e->enable && e->bits) {
442113783Sorion		int mask, max, val, reg;
44350724Scg
444113783Sorion		reg = (e->reg >= 0) ? e->reg : -e->reg;	/* AC97 register    */
445113783Sorion		max = (1 << e->bits) - 1;		/* actual range	    */
446113783Sorion		mask = (max << 8) | max;		/* bits of interest */
447113783Sorion
44858384Scg		if (!e->stereo)
44958384Scg			right = left;
450113783Sorion
451113783Sorion		/*
452113783Sorion		 * Invert the range if the polarity requires so,
453113783Sorion		 * then scale to 0..max-1 to compute the value to
454113783Sorion		 * write into the codec, and scale back to 0..100
455113783Sorion		 * for the return value.
456113783Sorion		 */
45750724Scg		if (e->reg > 0) {
45850724Scg			left = 100 - left;
45950724Scg			right = 100 - right;
46050724Scg		}
46150724Scg
46250724Scg		left = (left * max) / 100;
46350724Scg		right = (right * max) / 100;
46450724Scg
46550724Scg		val = (left << 8) | right;
46650724Scg
46750724Scg		left = (left * 100) / max;
46850724Scg		right = (right * 100) / max;
46950724Scg
47050724Scg		if (e->reg > 0) {
47150724Scg			left = 100 - left;
47250724Scg			right = 100 - right;
47350724Scg		}
47450724Scg
475113783Sorion		/*
476113783Sorion		 * For mono controls, trim val and mask, also taking
477119209Sorion		 * care of e->ofs (offset of control field).
478113783Sorion		 */
479113783Sorion		if (e->ofs) {
48050724Scg			val &= max;
48150724Scg			val <<= e->ofs;
482113783Sorion			mask = (max << e->ofs);
48350724Scg		}
484113783Sorion
485113783Sorion		/*
486113783Sorion		 * If we have a mute bit, add it to the mask and
487113783Sorion		 * update val and set mute if both channels require a
488113783Sorion		 * zero volume.
489113783Sorion		 */
490113783Sorion		if (e->mute == 1) {
491113783Sorion			mask |= AC97_MUTE;
492113783Sorion			if (left == 0 && right == 0)
493113783Sorion				val = AC97_MUTE;
494113783Sorion		}
495113783Sorion
496113783Sorion		/*
497113783Sorion		 * If the mask bit is set, do not alter the other bits.
498113783Sorion		 */
49974763Scg		snd_mtxlock(codec->lock);
500113783Sorion		if (e->mask) {
501149949Snetchild			int cur = ac97_rdcd(codec, reg);
502113783Sorion			val |= cur & ~(mask);
503113783Sorion		}
504109818Sorion		ac97_wrcd(codec, reg, val);
50574763Scg		snd_mtxunlock(codec->lock);
50650724Scg		return left | (right << 8);
50766013Scg	} else {
508149949Snetchild#if 0
509149949Snetchild		printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable);
510149949Snetchild#endif
51158384Scg		return -1;
51266013Scg	}
51350724Scg}
51450724Scg
515102302Sorionstatic void
516102302Sorionac97_fix_auxout(struct ac97_info *codec)
517102302Sorion{
518119375Sorion	int keep_ogain;
519119375Sorion
520119209Sorion	/*
521119375Sorion	 * By default, The ac97 aux_out register (0x04) corresponds to OSS's
522119375Sorion	 * OGAIN setting.
523102302Sorion	 *
524119375Sorion	 * We first check whether aux_out is a valid register.  If not
525119375Sorion	 * we may not want to keep ogain.
526119209Sorion	 */
527119375Sorion	keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000;
528119209Sorion
529119209Sorion	/*
530119209Sorion	 * Determine what AUX_OUT really means, it can be:
531119209Sorion	 *
532102302Sorion	 * 1. Headphone out.
533102302Sorion	 * 2. 4-Channel Out
534102302Sorion	 * 3. True line level out (effectively master volume).
535102302Sorion	 *
536102302Sorion	 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
537102302Sorion	 */
538119209Sorion	if (codec->extcaps & AC97_EXTCAP_SDAC &&
539119209Sorion	    ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
540119375Sorion		codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND;
541119375Sorion		keep_ogain = 1;
542102302Sorion	}
543119375Sorion
544119375Sorion	if (keep_ogain == 0) {
545119375Sorion		bzero(&codec->mix[SOUND_MIXER_OGAIN],
546119375Sorion		      sizeof(codec->mix[SOUND_MIXER_OGAIN]));
547119375Sorion	}
548102302Sorion}
549102302Sorion
550119209Sorionstatic void
551119209Sorionac97_fix_tone(struct ac97_info *codec)
552119209Sorion{
553167256Sariff	/*
554167256Sariff	 * YMF chips does not indicate tone and 3D enhancement capability
555167256Sariff	 * in the AC97_REG_RESET register.
556167256Sariff	 */
557167256Sariff	switch (codec->id) {
558167256Sariff	case 0x594d4800:	/* YMF743 */
559167256Sariff	case 0x594d4803:	/* YMF753 */
560167256Sariff		codec->caps |= AC97_CAP_TONE;
561167256Sariff		codec->se |= 0x04;
562167256Sariff		break;
563167256Sariff	case 0x594d4802:	/* YMF752 */
564167256Sariff		codec->se |= 0x04;
565167256Sariff		break;
566167256Sariff	default:
567167256Sariff		break;
568167256Sariff	}
569167256Sariff
570119209Sorion	/* Hide treble and bass if they don't exist */
571119209Sorion	if ((codec->caps & AC97_CAP_TONE) == 0) {
572119209Sorion		bzero(&codec->mix[SOUND_MIXER_BASS],
573119209Sorion		      sizeof(codec->mix[SOUND_MIXER_BASS]));
574119209Sorion		bzero(&codec->mix[SOUND_MIXER_TREBLE],
575119209Sorion		      sizeof(codec->mix[SOUND_MIXER_TREBLE]));
576119209Sorion	}
577119209Sorion}
578119209Sorion
579111679Sorionstatic const char*
580111679Sorionac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
581111679Sorion{
582111679Sorion	if (cname == NULL) {
583111679Sorion		sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
584111679Sorion		return buf;
585111679Sorion	}
586111679Sorion
587111679Sorion	if (vname == NULL) vname = "Unknown";
588119209Sorion
589111679Sorion	if (bootverbose) {
590111679Sorion		sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
591111679Sorion	} else {
592111679Sorion		sprintf(buf, "%s %s AC97 Codec", vname, cname);
593111679Sorion	}
594111679Sorion	return buf;
595111679Sorion}
596111679Sorion
59750724Scgstatic unsigned
59858384Scgac97_initmixer(struct ac97_info *codec)
59950724Scg{
600109818Sorion	ac97_patch codec_patch;
601111679Sorion	const char *cname, *vname;
602111679Sorion	char desc[80];
603111679Sorion	u_int8_t model, step;
604149949Snetchild	unsigned i, j, k, bit, old;
60550724Scg	u_int32_t id;
606149949Snetchild	int reg;
60750724Scg
60874763Scg	snd_mtxlock(codec->lock);
60970134Scg	codec->count = AC97_INIT(codec->methods, codec->devinfo);
61070134Scg	if (codec->count == 0) {
61170134Scg		device_printf(codec->dev, "ac97 codec init failed\n");
61274763Scg		snd_mtxunlock(codec->lock);
61370134Scg		return ENODEV;
61470134Scg	}
61567652Scg
616109818Sorion	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
61795499Sorion	ac97_reset(codec);
618109818Sorion	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
61950724Scg
620109818Sorion	i = ac97_rdcd(codec, AC97_REG_RESET);
621149949Snetchild	j = ac97_rdcd(codec, AC97_REG_RESET);
622170342Sariff	k = ac97_rdcd(codec, AC97_REG_RESET);
623149949Snetchild	/*
624149949Snetchild	 * Let see if this codec can return consistent value.
625149949Snetchild	 * If not, turn on aggressive read workaround
626149949Snetchild	 * (STAC9704 comes in mind).
627149949Snetchild	 */
628170342Sariff	if (i != j || j != k) {
629149949Snetchild		codec->flags |= AC97_F_RDCD_BUG;
630149949Snetchild		i = ac97_rdcd(codec, AC97_REG_RESET);
631149949Snetchild	}
63250724Scg	codec->caps = i & 0x03ff;
63350724Scg	codec->se =  (i & 0x7c00) >> 10;
63450724Scg
635109818Sorion	id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
63658905Scg	if (id == 0 || id == 0xffffffff) {
63758905Scg		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
63874763Scg		snd_mtxunlock(codec->lock);
63958905Scg		return ENODEV;
64058905Scg	}
64150724Scg
642111679Sorion	codec->id = id;
643168861Sariff	codec->subvendor = (u_int32_t)pci_get_subdevice(codec->dev) << 16;
644168861Sariff	codec->subvendor |= (u_int32_t)pci_get_subvendor(codec->dev) &
645168861Sariff	    0x0000ffff;
64665490Scg	codec->noext = 0;
647109818Sorion	codec_patch = NULL;
648111679Sorion
649111679Sorion	cname = NULL;
650111679Sorion	model = step = 0;
65165490Scg	for (i = 0; ac97codecid[i].id; i++) {
652111679Sorion		u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
653111679Sorion		if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
65465490Scg			codec->noext = ac97codecid[i].noext;
655109818Sorion			codec_patch = ac97codecid[i].patch;
656111679Sorion			cname = ac97codecid[i].name;
657111679Sorion			model = (id & modelmask) & 0xff;
658111679Sorion			step = (id & ~modelmask) & 0xff;
659111679Sorion			break;
66065490Scg		}
66165490Scg	}
66258521Scg
663111679Sorion	vname = NULL;
664111679Sorion	for (i = 0; ac97vendorid[i].id; i++) {
665111679Sorion		if (ac97vendorid[i].id == (id & 0xffffff00)) {
666111679Sorion			vname = ac97vendorid[i].name;
667111679Sorion			break;
668111679Sorion		}
669111679Sorion	}
670111679Sorion
67170324Scg	codec->extcaps = 0;
67270324Scg	codec->extid = 0;
67370324Scg	codec->extstat = 0;
67470324Scg	if (!codec->noext) {
675109818Sorion		i = ac97_rdcd(codec, AC97_REGEXT_ID);
67670324Scg		if (i != 0xffff) {
67770324Scg			codec->extcaps = i & 0x3fff;
67870324Scg			codec->extid =  (i & 0xc000) >> 14;
679109818Sorion			codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
68070324Scg		}
68158521Scg	}
68258521Scg
683170342Sariff	for (i = 0; i < AC97_MIXER_SIZE; i++) {
684102302Sorion		codec->mix[i] = ac97mixtable_default[i];
685102302Sorion	}
686102302Sorion	ac97_fix_auxout(codec);
687119209Sorion	ac97_fix_tone(codec);
688109818Sorion	if (codec_patch)
689109818Sorion		codec_patch(codec);
690102302Sorion
691170342Sariff	for (i = 0; i < AC97_MIXER_SIZE; i++) {
69266307Scg		k = codec->noext? codec->mix[i].enable : 1;
693149949Snetchild		reg = codec->mix[i].reg;
694149949Snetchild		if (reg < 0)
695149949Snetchild			reg = -reg;
696149949Snetchild		if (k && reg) {
697149949Snetchild			j = old = ac97_rdcd(codec, reg);
698149949Snetchild			/*
699149949Snetchild			 * Test for mute bit (except for AC97_MIX_TONE,
700149949Snetchild			 * where we simply assume it as available).
701149949Snetchild			 */
702149949Snetchild			if (codec->mix[i].mute) {
703149949Snetchild				ac97_wrcd(codec, reg, j | 0x8000);
704149949Snetchild				j = ac97_rdcd(codec, reg);
705149949Snetchild			} else
706149949Snetchild				j |= 0x8000;
707148602Snetchild			if ((j & 0x8000)) {
708149949Snetchild				/*
709149949Snetchild				 * Test whether the control width should be
710149949Snetchild				 * 4, 5 or 6 bit. For 5bit register, we should
711149949Snetchild				 * test it whether it's really 5 or 6bit. Leave
712149949Snetchild				 * 4bit register alone, because sometimes an
713149949Snetchild				 * attempt to write past 4th bit may cause
714149949Snetchild				 * incorrect result especially for AC97_MIX_BEEP
715149949Snetchild				 * (ac97 2.3).
716149949Snetchild				 */
717149949Snetchild				bit = codec->mix[i].bits;
718149949Snetchild				if (bit == 5)
719149949Snetchild					bit++;
720149949Snetchild				j = ((1 << bit) - 1) << codec->mix[i].ofs;
721149949Snetchild				ac97_wrcd(codec, reg,
722149949Snetchild					j | (codec->mix[i].mute ? 0x8000 : 0));
723149949Snetchild				k = ac97_rdcd(codec, reg) & j;
724149949Snetchild				k >>= codec->mix[i].ofs;
725149949Snetchild				if (reg == AC97_MIX_TONE &&
726149949Snetchild							((k & 0x0001) == 0x0000))
727148602Snetchild					k >>= 1;
728149949Snetchild				for (j = 0; k >> j; j++)
729149949Snetchild					;
730148602Snetchild				if (j != 0) {
731149949Snetchild#if 0
732149949Snetchild					device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n",
733149949Snetchild						i, k, bit, codec->mix[i].bits, j);
734149949Snetchild#endif
735148602Snetchild					codec->mix[i].enable = 1;
736148602Snetchild					codec->mix[i].bits = j;
737153865Sariff				} else if (reg == AC97_MIX_BEEP) {
738153865Sariff					/*
739153865Sariff					 * Few codec such as CX20468-21 does
740153865Sariff					 * have this control register, although
741153865Sariff					 * the only usable part is the mute bit.
742153865Sariff					 */
743153865Sariff					codec->mix[i].enable = 1;
744148602Snetchild				} else
745148602Snetchild					codec->mix[i].enable = 0;
746148602Snetchild			} else
747148602Snetchild				codec->mix[i].enable = 0;
748149949Snetchild			ac97_wrcd(codec, reg, old);
74966013Scg		}
750148602Snetchild#if 0
751148602Snetchild		printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits);
752148602Snetchild#endif
75366013Scg	}
75450724Scg
755111679Sorion	device_printf(codec->dev, "<%s>\n",
756111679Sorion		      ac97_hw_desc(codec->id, vname, cname, desc));
757110258Sorion
75850724Scg	if (bootverbose) {
759149949Snetchild		if (codec->flags & AC97_F_RDCD_BUG)
760149949Snetchild			device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n");
761111679Sorion		device_printf(codec->dev, "Codec features ");
76258384Scg		for (i = j = 0; i < 10; i++)
76358384Scg			if (codec->caps & (1 << i))
76458384Scg				printf("%s%s", j++? ", " : "", ac97feature[i]);
76558384Scg		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
76658384Scg		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
76758384Scg
76858384Scg		if (codec->extcaps != 0 || codec->extid) {
769111679Sorion			device_printf(codec->dev, "%s codec",
770111679Sorion				      codec->extid? "Secondary" : "Primary");
77158384Scg			if (codec->extcaps)
77258384Scg				printf(" extended features ");
77358384Scg			for (i = j = 0; i < 14; i++)
77458384Scg				if (codec->extcaps & (1 << i))
77558384Scg					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
77658384Scg			printf("\n");
77750724Scg		}
77850724Scg	}
77950724Scg
780148602Snetchild	i = 0;
781148602Snetchild	while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) {
782148602Snetchild		if (++i == 100) {
783148602Snetchild			device_printf(codec->dev, "ac97 codec reports dac not ready\n");
784148602Snetchild			break;
785148602Snetchild		}
786148602Snetchild		DELAY(1000);
787148602Snetchild	}
788148602Snetchild	if (bootverbose)
789148602Snetchild		device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i);
79074763Scg	snd_mtxunlock(codec->lock);
79150724Scg	return 0;
79250724Scg}
79350724Scg
79467652Scgstatic unsigned
79567652Scgac97_reinitmixer(struct ac97_info *codec)
79667652Scg{
79774763Scg	snd_mtxlock(codec->lock);
79870134Scg	codec->count = AC97_INIT(codec->methods, codec->devinfo);
79970134Scg	if (codec->count == 0) {
80070134Scg		device_printf(codec->dev, "ac97 codec init failed\n");
80174763Scg		snd_mtxunlock(codec->lock);
80270134Scg		return ENODEV;
80370134Scg	}
80467652Scg
805109818Sorion	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
80695499Sorion	ac97_reset(codec);
807109818Sorion	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
80867652Scg
80967652Scg	if (!codec->noext) {
810109818Sorion		ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
811109818Sorion		if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
81287623Sguido		    != codec->extstat)
81367652Scg			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
81487623Sguido				      codec->extstat,
815109818Sorion				      ac97_rdcd(codec, AC97_REGEXT_STAT) &
816109818Sorion				      AC97_EXTCAPS);
81767652Scg	}
81867652Scg
819109818Sorion	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
82067652Scg		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
82174763Scg	snd_mtxunlock(codec->lock);
82267652Scg	return 0;
82367652Scg}
82467652Scg
82550724Scgstruct ac97_info *
82670134Scgac97_create(device_t dev, void *devinfo, kobj_class_t cls)
82750724Scg{
82850724Scg	struct ac97_info *codec;
829193640Sariff	int i;
83050724Scg
831170873Sariff	codec = malloc(sizeof(*codec), M_AC97, M_WAITOK | M_ZERO);
832170342Sariff	snprintf(codec->name, sizeof(codec->name), "%s:ac97",
833170342Sariff	    device_get_nameunit(dev));
83493816Sjhb	codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
835162738Sariff	codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO);
83670134Scg	codec->dev = dev;
83770134Scg	codec->devinfo = devinfo;
83878668Scg	codec->flags = 0;
839193640Sariff
840162738Sariff	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
841193640Sariff	    "eapdinv", &i) == 0 && i != 0)
842193640Sariff		codec->flags |= AC97_F_EAPD_INV;
843193640Sariff
844193640Sariff	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
845193640Sariff	    "softpcmvol", &i) == 0 && i != 0)
846193640Sariff		pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL);
847193640Sariff
84850724Scg	return codec;
84950724Scg}
85050724Scg
85165340Scgvoid
85265340Scgac97_destroy(struct ac97_info *codec)
85365340Scg{
85474763Scg	snd_mtxlock(codec->lock);
85570134Scg	if (codec->methods != NULL)
85670134Scg		kobj_delete(codec->methods, M_AC97);
85774763Scg	snd_mtxfree(codec->lock);
85870134Scg	free(codec, M_AC97);
85965340Scg}
86065340Scg
86178668Scgvoid
86278668Scgac97_setflags(struct ac97_info *codec, u_int32_t val)
86378668Scg{
86478668Scg	codec->flags = val;
86578668Scg}
86678668Scg
86778668Scgu_int32_t
86878668Scgac97_getflags(struct ac97_info *codec)
86978668Scg{
87078668Scg	return codec->flags;
87178668Scg}
87278668Scg
87370134Scg/* -------------------------------------------------------------------- */
87470134Scg
87550724Scgstatic int
876164614Sariffsysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS)
877164614Sariff{
878164614Sariff	struct ac97_info *codec;
879164614Sariff	int ea, inv, err = 0;
880164614Sariff	u_int16_t val;
881164614Sariff
882164614Sariff	codec = oidp->oid_arg1;
883164614Sariff	if (codec == NULL || codec->id == 0 || codec->lock == NULL)
884164614Sariff		return EINVAL;
885164614Sariff	snd_mtxlock(codec->lock);
886164614Sariff	val = ac97_rdcd(codec, AC97_REG_POWER);
887164614Sariff	inv = (codec->flags & AC97_F_EAPD_INV) ? 0 : 1;
888164614Sariff	ea = (val >> 15) ^ inv;
889164614Sariff	snd_mtxunlock(codec->lock);
890170289Sdwmalone	err = sysctl_handle_int(oidp, &ea, 0, req);
891164614Sariff	if (err == 0 && req->newptr != NULL) {
892164614Sariff		if (ea != 0 && ea != 1)
893164614Sariff			return EINVAL;
894164614Sariff		if (ea != ((val >> 15) ^ inv)) {
895164614Sariff			snd_mtxlock(codec->lock);
896164614Sariff			ac97_wrcd(codec, AC97_REG_POWER, val ^ 0x8000);
897164614Sariff			snd_mtxunlock(codec->lock);
898164614Sariff		}
899164614Sariff	}
900164614Sariff	return err;
901164614Sariff}
902164614Sariff
903164614Sariffstatic void
904164614Sariffac97_init_sysctl(struct ac97_info *codec)
905164614Sariff{
906164614Sariff	u_int16_t orig, val;
907164614Sariff
908164614Sariff	if (codec == NULL || codec->dev == NULL)
909164614Sariff		return;
910164614Sariff	snd_mtxlock(codec->lock);
911164614Sariff	orig = ac97_rdcd(codec, AC97_REG_POWER);
912164614Sariff	ac97_wrcd(codec, AC97_REG_POWER, orig ^ 0x8000);
913164614Sariff	val = ac97_rdcd(codec, AC97_REG_POWER);
914164614Sariff	ac97_wrcd(codec, AC97_REG_POWER, orig);
915164614Sariff	snd_mtxunlock(codec->lock);
916164614Sariff	if ((val & 0x8000) == (orig & 0x8000))
917164614Sariff		return;
918164614Sariff	SYSCTL_ADD_PROC(device_get_sysctl_ctx(codec->dev),
919164614Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(codec->dev)),
920164614Sariff            OID_AUTO, "eapd", CTLTYPE_INT | CTLFLAG_RW,
921164614Sariff	    codec, sizeof(codec), sysctl_hw_snd_ac97_eapd,
922164614Sariff	    "I", "AC97 External Amplifier");
923164614Sariff}
924164614Sariff
925164614Sariffstatic int
92674763Scgac97mix_init(struct snd_mixer *m)
92750724Scg{
92850724Scg	struct ac97_info *codec = mix_getdevinfo(m);
92966013Scg	u_int32_t i, mask;
93066013Scg
93158384Scg	if (codec == NULL)
93258384Scg		return -1;
93366013Scg
93458905Scg	if (ac97_initmixer(codec))
93558905Scg		return -1;
93666013Scg
937162738Sariff	switch (codec->id) {
938162738Sariff	case 0x41445374:	/* AD1981B */
939173039Sariff		switch (codec->subvendor) {
940173039Sariff		case 0x02d91014:
941168861Sariff			/*
942168861Sariff			 * IBM Thinkcentre:
943173039Sariff			 *
944173039Sariff			 * Tie "ogain" and "phout" to "vol" since its
945168861Sariff			 * master volume is basically useless and can't
946168861Sariff			 * control anything.
947168861Sariff			 */
948164614Sariff			mask = 0;
949164614Sariff			if (codec->mix[SOUND_MIXER_OGAIN].enable)
950164614Sariff				mask |= SOUND_MASK_OGAIN;
951164614Sariff			if (codec->mix[SOUND_MIXER_PHONEOUT].enable)
952164614Sariff				mask |= SOUND_MASK_PHONEOUT;
953164614Sariff			if (codec->mix[SOUND_MIXER_VOLUME].enable)
954164614Sariff				mix_setparentchild(m, SOUND_MIXER_VOLUME,
955164614Sariff				    mask);
956164614Sariff			else {
957164614Sariff				mix_setparentchild(m, SOUND_MIXER_VOLUME,
958164614Sariff				    mask);
959164614Sariff				mix_setrealdev(m, SOUND_MIXER_VOLUME,
960164614Sariff				    SOUND_MIXER_NONE);
961164614Sariff			}
962173039Sariff			break;
963173039Sariff		case 0x099c103c:
964173039Sariff			/*
965173039Sariff			 * HP nx6110:
966173039Sariff			 *
967173039Sariff			 * By default, "vol" is controlling internal speakers
968173039Sariff			 * (not a master volume!) and "ogain" is controlling
969173039Sariff			 * headphone. Enable dummy "phout" so it can be
970173039Sariff			 * remapped to internal speakers and virtualize
971173039Sariff			 * "vol" to control both.
972173039Sariff			 */
973173039Sariff			codec->mix[SOUND_MIXER_OGAIN].enable = 1;
974173039Sariff			codec->mix[SOUND_MIXER_PHONEOUT].enable = 1;
975173039Sariff			mix_setrealdev(m, SOUND_MIXER_PHONEOUT,
976173039Sariff			    SOUND_MIXER_VOLUME);
977173039Sariff			mix_setrealdev(m, SOUND_MIXER_VOLUME,
978173039Sariff			    SOUND_MIXER_NONE);
979173039Sariff			mix_setparentchild(m, SOUND_MIXER_VOLUME,
980173039Sariff			    SOUND_MASK_OGAIN | SOUND_MASK_PHONEOUT);
981173039Sariff			break;
982173039Sariff		default:
983173039Sariff			break;
984162738Sariff		}
985162738Sariff		break;
986162738Sariff	case 0x434d4941:	/* CMI9738 */
987162738Sariff	case 0x434d4961:	/* CMI9739 */
988162738Sariff	case 0x434d4978:	/* CMI9761 */
989162738Sariff	case 0x434d4982:	/* CMI9761 */
990162738Sariff	case 0x434d4983:	/* CMI9761 */
991162738Sariff		bzero(&codec->mix[SOUND_MIXER_PCM],
992162738Sariff		    sizeof(codec->mix[SOUND_MIXER_PCM]));
993170207Sariff		pcm_setflags(codec->dev, pcm_getflags(codec->dev) |
994170207Sariff		    SD_F_SOFTPCMVOL);
995162738Sariff		/* XXX How about master volume ? */
996162738Sariff		break;
997162738Sariff	default:
998162738Sariff		break;
999162738Sariff	}
1000162738Sariff
1001193640Sariff	if (pcm_getflags(codec->dev) & SD_F_SOFTPCMVOL)
1002193640Sariff		ac97_wrcd(codec, AC97_MIX_PCM, 0);
1003162738Sariff#if 0
1004162738Sariff	/* XXX For the sake of debugging purposes */
1005162738Sariff	mix_setparentchild(m, SOUND_MIXER_VOLUME,
1006162738Sariff	    SOUND_MASK_PCM | SOUND_MASK_CD);
1007162738Sariff	mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
1008162738Sariff	ac97_wrcd(codec, AC97_MIX_MASTER, 0);
1009162738Sariff#endif
1010162738Sariff
101166013Scg	mask = 0;
1012170342Sariff	for (i = 0; i < AC97_MIXER_SIZE; i++)
101366013Scg		mask |= codec->mix[i].enable? 1 << i : 0;
101466013Scg	mix_setdevs(m, mask);
101566013Scg
101666013Scg	mask = 0;
1017170342Sariff	for (i = 0; i < AC97_MIXER_SIZE; i++)
101866013Scg		mask |= codec->mix[i].recidx? 1 << i : 0;
101966013Scg	mix_setrecdevs(m, mask);
1020164614Sariff
1021164614Sariff	ac97_init_sysctl(codec);
1022164614Sariff
102350724Scg	return 0;
102450724Scg}
102550724Scg
102650724Scgstatic int
102774763Scgac97mix_uninit(struct snd_mixer *m)
102865340Scg{
102965340Scg	struct ac97_info *codec = mix_getdevinfo(m);
103066013Scg
103165340Scg	if (codec == NULL)
103265340Scg		return -1;
103365340Scg	/*
103465340Scg	if (ac97_uninitmixer(codec))
103565340Scg		return -1;
103665340Scg	*/
103765340Scg	ac97_destroy(codec);
103865340Scg	return 0;
103965340Scg}
104065340Scg
104165340Scgstatic int
104274763Scgac97mix_reinit(struct snd_mixer *m)
104367652Scg{
104467652Scg	struct ac97_info *codec = mix_getdevinfo(m);
104567652Scg
104667652Scg	if (codec == NULL)
104767652Scg		return -1;
104867652Scg	return ac97_reinitmixer(codec);
104967652Scg}
105067652Scg
105167652Scgstatic int
105274763Scgac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
105350724Scg{
105450724Scg	struct ac97_info *codec = mix_getdevinfo(m);
105566013Scg
1056170342Sariff	if (codec == NULL || dev >= AC97_MIXER_SIZE)
105758384Scg		return -1;
105850724Scg	return ac97_setmixer(codec, dev, left, right);
105950724Scg}
106050724Scg
1061193640Sariffstatic u_int32_t
106274763Scgac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
106350724Scg{
106450724Scg	int i;
106550724Scg	struct ac97_info *codec = mix_getdevinfo(m);
106666013Scg
106758384Scg	if (codec == NULL)
106858384Scg		return -1;
1069170342Sariff	for (i = 0; i < AC97_MIXER_SIZE; i++)
107058384Scg		if ((src & (1 << i)) != 0)
107158384Scg			break;
1072193640Sariff	return (ac97_setrecsrc(codec, i) == 0)? 1U << i : 0xffffffffU;
107350724Scg}
107450724Scg
107570134Scgstatic kobj_method_t ac97mixer_methods[] = {
107670134Scg    	KOBJMETHOD(mixer_init,		ac97mix_init),
107770134Scg    	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
107870134Scg    	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
107970134Scg    	KOBJMETHOD(mixer_set,		ac97mix_set),
108070134Scg    	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
1081193640Sariff	KOBJMETHOD_END
108250724Scg};
108370134ScgMIXER_DECLARE(ac97mixer);
108450724Scg
108570134Scg/* -------------------------------------------------------------------- */
108670134Scg
108770134Scgkobj_class_t
108870134Scgac97_getmixerclass(void)
108970134Scg{
109070134Scg	return &ac97mixer_class;
109170134Scg}
1092