xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision ebccf1e3a6b11b97cbf5f813dd76636e892a9035)
1 /*-
2  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <dev/sound/pcm/sound.h>
28 #include <dev/sound/pcm/ac97.h>
29 #include <dev/sound/pcm/ac97_patch.h>
30 
31 #include "mixer_if.h"
32 
33 SND_DECLARE_FILE("$FreeBSD$");
34 
35 MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
36 
37 struct ac97mixtable_entry {
38 	int	 reg:8;		/* register index		*/
39 				/* reg < 0 if inverted polarity	*/
40 	unsigned bits:4;	/* width of control field	*/
41 	unsigned ofs:4;		/* offset (only if stereo=0)	*/
42 	unsigned stereo:1;	/* set for stereo controls	*/
43 	unsigned mute:1;	/* bit15 is MUTE		*/
44 	unsigned recidx:4;	/* index in rec mux		*/
45 	unsigned mask:1;	/* use only masked bits		*/
46 	unsigned enable:1;	/* entry is enabled		*/
47 };
48 
49 #define AC97_NAMELEN	16
50 struct ac97_info {
51 	kobj_t methods;
52 	device_t dev;
53 	void *devinfo;
54 	u_int32_t id;
55 	unsigned count, caps, se, extcaps, extid, extstat, noext:1;
56 	u_int32_t flags;
57 	struct ac97mixtable_entry mix[32];
58 	char name[AC97_NAMELEN];
59 	struct mtx *lock;
60 };
61 
62 struct ac97_vendorid {
63 	u_int32_t   id;
64 	const char *name;
65 };
66 
67 struct ac97_codecid {
68 	u_int32_t  id;
69 	u_int8_t   stepmask;
70 	u_int8_t   noext:1;
71 	char 	  *name;
72 	ac97_patch patch;
73 };
74 
75 static const struct ac97mixtable_entry ac97mixtable_default[32] = {
76     /*	[offset]			reg	     bits of st mu re mk en */
77 	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0, 1 },
78 	[SOUND_MIXER_OGAIN]	= { AC97_MIX_AUXOUT, 	5, 0, 1, 1, 0, 0, 0 },
79 	[SOUND_MIXER_PHONEOUT]	= { AC97_MIX_MONO, 	5, 0, 0, 1, 7, 0, 0 },
80 	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1, 0 },
81 	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1, 0 },
82 	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0, 1 },
83 	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0, 0 },
84 	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0, 1 },
85 	[SOUND_MIXER_PHONEIN]	= { AC97_MIX_PHONE, 	5, 0, 0, 1, 8, 0, 0 },
86 	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 1, 1 },
87 #if 0
88 	/* use igain for the mic 20dB boost */
89 	[SOUND_MIXER_IGAIN] 	= { -AC97_MIX_MIC, 	1, 6, 0, 0, 0, 1, 1 },
90 #endif
91 	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
92 	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
93 	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
94 	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
95 };
96 
97 static const struct ac97_vendorid ac97vendorid[] = {
98 	{ 0x41445300, "Analog Devices" },
99 	{ 0x414b4d00, "Asahi Kasei" },
100 	{ 0x414c4300, "Realtek" },
101 	{ 0x414c4700, "Avance Logic" },
102 	{ 0x43525900, "Cirrus Logic" },
103 	{ 0x434d4900, "C-Media Electronics" },
104 	{ 0x43585400, "Conexant" },
105 	{ 0x44543000, "Diamond Technology" },
106 	{ 0x454d4300, "eMicro" },
107 	{ 0x45838300, "ESS Technology" },
108 	{ 0x48525300, "Intersil" },
109 	{ 0x49434500, "ICEnsemble" },
110 	{ 0x49544500, "ITE, Inc." },
111 	{ 0x4e534300, "National Semiconductor" },
112 	{ 0x50534300, "Philips Semiconductor" },
113 	{ 0x83847600, "SigmaTel" },
114 	{ 0x53494c00, "Silicon Laboratories" },
115 	{ 0x54524100, "TriTech" },
116 	{ 0x54584e00, "Texas Instruments" },
117 	{ 0x56494100, "VIA Technologies" },
118 	{ 0x57454300, "Winbond" },
119 	{ 0x574d4c00, "Wolfson" },
120 	{ 0x594d4800, "Yamaha" },
121 	{ 0x01408300, "Creative" },
122 	{ 0x00000000, NULL }
123 };
124 
125 static struct ac97_codecid ac97codecid[] = {
126 	{ 0x41445303, 0x00, 0, "AD1819",	0 },
127 	{ 0x41445340, 0x00, 0, "AD1881",	0 },
128 	{ 0x41445348, 0x00, 0, "AD1881A",	0 },
129 	{ 0x41445360, 0x00, 0, "AD1885",	0 },
130 	{ 0x41445361, 0x00, 0, "AD1886", 	ad1886_patch },
131 	{ 0x41445362, 0x00, 0, "AD1887", 	0 },
132 	{ 0x41445363, 0x00, 0, "AD1886A", 	0 },
133 	{ 0x41445368, 0x00, 0, "AD1888", 	ad198x_patch },
134 	{ 0x41445370, 0x00, 0, "AD1980",	ad198x_patch },
135 	{ 0x41445372, 0x00, 0, "AD1981A",	0 },
136 	{ 0x41445374, 0x00, 0, "AD1981B",	0 },
137 	{ 0x41445375, 0x00, 0, "AD1985",	ad198x_patch },
138 	{ 0x414b4d00, 0x00, 1, "AK4540", 	0 },
139 	{ 0x414b4d01, 0x00, 1, "AK4542", 	0 },
140 	{ 0x414b4d02, 0x00, 1, "AK4543", 	0 },
141 	{ 0x414b4d06, 0x00, 0, "AK4544A",	0 },
142 	{ 0x454b4d07, 0x00, 0, "AK4545",	0 },
143 	{ 0x414c4320, 0x0f, 0, "ALC100",	0 },
144 	{ 0x414c4730, 0x0f, 0, "ALC101",	0 },
145 	{ 0x414c4710, 0x0f, 0, "ALC200", 	0 },
146 	{ 0x414c4740, 0x0f, 0, "ALC202", 	0 },
147 	{ 0x414c4720, 0x0f, 0, "ALC650", 	0 },
148 	{ 0x414c4760, 0x0f, 0, "ALC655",	0 },
149 	{ 0x414c4780, 0x0f, 0, "ALC658",	0 },
150 	{ 0x414c4790, 0x0f, 0, "ALC850",	0 },
151 	{ 0x43525900, 0x07, 0, "CS4297", 	0 },
152 	{ 0x43525910, 0x07, 0, "CS4297A", 	0 },
153 	{ 0x43525920, 0x07, 0, "CS4294/98",	0 },
154 	{ 0x4352592d, 0x07, 0, "CS4294",	0 },
155 	{ 0x43525930, 0x07, 0, "CS4299",	0 },
156 	{ 0x43525940, 0x07, 0, "CS4201",	0 },
157 	{ 0x43525958, 0x07, 0, "CS4205",	0 },
158 	{ 0x43525960, 0x07, 0, "CS4291A",	0 },
159 	{ 0x434d4961, 0x00, 0, "CMI9739",	0 },
160 	{ 0x434d4941, 0x00, 0, "CMI9738",	0 },
161 	{ 0x43585421, 0x00, 0, "HSD11246",	0 },
162 	{ 0x43585428, 0x07, 0, "CX20468",	0 },
163 	{ 0x44543000, 0x00, 0, "DT0398",	0 },
164 	{ 0x454d4323, 0x00, 0, "EM28023",	0 },
165 	{ 0x454d4328, 0x00, 0, "EM28028",	0 },
166 	{ 0x45838308, 0x00, 0, "ES1988",	0 }, /* Formerly ES1921(?) */
167 	{ 0x48525300, 0x00, 0, "HMP9701",	0 },
168 	{ 0x49434501, 0x00, 0, "ICE1230",	0 },
169 	{ 0x49434511, 0x00, 0, "ICE1232",	0 },
170 	{ 0x49434514, 0x00, 0, "ICE1232A",	0 },
171 	{ 0x49434551, 0x03, 0, "VT1616",	0 }, /* Via badged ICE */
172 	{ 0x49544520, 0x00, 0, "ITE2226E",	0 },
173 	{ 0x49544560, 0x07, 0, "ITE2646E",	0 }, /* XXX: patch needed */
174 	{ 0x4e534340, 0x00, 0, "LM4540",	0 }, /* Spec blank on revid */
175 	{ 0x4e534343, 0x00, 0, "LM4543",	0 }, /* Ditto */
176 	{ 0x4e534346, 0x00, 0, "LM4546A",	0 },
177 	{ 0x4e534348, 0x00, 0, "LM4548A",	0 },
178 	{ 0x4e534331, 0x00, 0, "LM4549",	0 },
179 	{ 0x4e534349, 0x00, 0, "LM4549A",	0 },
180 	{ 0x4e534350, 0x00, 0, "LM4550",	0 },
181 	{ 0x50534301, 0x00, 0, "UCB1510",	0 },
182 	{ 0x50534304, 0x00, 0, "UCB1400",	0 },
183 	{ 0x83847600, 0x00, 0, "STAC9700/83/84",	0 },
184 	{ 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
185 	{ 0x83847605, 0x00, 0, "STAC9704",	0 },
186 	{ 0x83847608, 0x00, 0, "STAC9708/11",	0 },
187 	{ 0x83847609, 0x00, 0, "STAC9721/23",	0 },
188 	{ 0x83847644, 0x00, 0, "STAC9744/45",	0 },
189 	{ 0x83847650, 0x00, 0, "STAC9750/51",	0 },
190 	{ 0x83847652, 0x00, 0, "STAC9752/53",	0 },
191 	{ 0x83847656, 0x00, 0, "STAC9756/57",	0 },
192 	{ 0x83847658, 0x00, 0, "STAC9758/59",	0 },
193 	{ 0x83847660, 0x00, 0, "STAC9760/61",	0 }, /* Extrapolated */
194 	{ 0x83847662, 0x00, 0, "STAC9762/63",	0 }, /* Extrapolated */
195 	{ 0x53494c22, 0x00, 0, "Si3036",	0 },
196 	{ 0x53494c23, 0x00, 0, "Si3038",	0 },
197 	{ 0x54524103, 0x00, 0, "TR28023",	0 }, /* Extrapolated */
198 	{ 0x54524106, 0x00, 0, "TR28026",	0 },
199 	{ 0x54524108, 0x00, 0, "TR28028",	0 },
200 	{ 0x54524123, 0x00, 0, "TR28602",	0 },
201 	{ 0x54524e03, 0x07, 0, "TLV320AIC27",	0 },
202 	{ 0x54584e20, 0x00, 0, "TLC320AD90",	0 },
203 	{ 0x56494161, 0x00, 0, "VIA1612A",      0 },
204 	{ 0x574d4c00, 0x00, 0, "WM9701A",	0 },
205 	{ 0x574d4c03, 0x00, 0, "WM9703/4/7/8",	0 },
206 	{ 0x574d4c04, 0x00, 0, "WM9704Q",	0 },
207 	{ 0x574d4c05, 0x00, 0, "WM9705/10",	0 },
208 	{ 0x574d4d09, 0x00, 0, "WM9709",	0 },
209 	{ 0x574d4c12, 0x00, 0, "WM9711/12",	0 }, /* XXX: patch needed */
210 	{ 0x57454301, 0x00, 0, "W83971D",	0 },
211 	{ 0x594d4800, 0x00, 0, "YMF743",	0 },
212 	{ 0x594d4802, 0x00, 0, "YMF752",	0 },
213 	{ 0x594d4803, 0x00, 0, "YMF753",	0 },
214 	{ 0x01408384, 0x00, 0, "EV1938",	0 },
215 	{ 0, 0, 0, NULL, 0 }
216 };
217 
218 static char *ac97enhancement[] = {
219 	"no 3D Stereo Enhancement",
220 	"Analog Devices Phat Stereo",
221 	"Creative Stereo Enhancement",
222 	"National Semi 3D Stereo Enhancement",
223 	"Yamaha Ymersion",
224 	"BBE 3D Stereo Enhancement",
225 	"Crystal Semi 3D Stereo Enhancement",
226 	"Qsound QXpander",
227 	"Spatializer 3D Stereo Enhancement",
228 	"SRS 3D Stereo Enhancement",
229 	"Platform Tech 3D Stereo Enhancement",
230 	"AKM 3D Audio",
231 	"Aureal Stereo Enhancement",
232 	"Aztech 3D Enhancement",
233 	"Binaura 3D Audio Enhancement",
234 	"ESS Technology Stereo Enhancement",
235 	"Harman International VMAx",
236 	"Nvidea 3D Stereo Enhancement",
237 	"Philips Incredible Sound",
238 	"Texas Instruments 3D Stereo Enhancement",
239 	"VLSI Technology 3D Stereo Enhancement",
240 	"TriTech 3D Stereo Enhancement",
241 	"Realtek 3D Stereo Enhancement",
242 	"Samsung 3D Stereo Enhancement",
243 	"Wolfson Microelectronics 3D Enhancement",
244 	"Delta Integration 3D Enhancement",
245 	"SigmaTel 3D Enhancement",
246 	"Reserved 27",
247 	"Rockwell 3D Stereo Enhancement",
248 	"Reserved 29",
249 	"Reserved 30",
250 	"Reserved 31"
251 };
252 
253 static char *ac97feature[] = {
254 	"mic channel",
255 	"reserved",
256 	"tone",
257 	"simulated stereo",
258 	"headphone",
259 	"bass boost",
260 	"18 bit DAC",
261 	"20 bit DAC",
262 	"18 bit ADC",
263 	"20 bit ADC"
264 };
265 
266 static char *ac97extfeature[] = {
267 	"variable rate PCM",
268 	"double rate PCM",
269 	"reserved 1",
270 	"variable rate mic",
271 	"reserved 2",
272 	"reserved 3",
273 	"center DAC",
274 	"surround DAC",
275 	"LFE DAC",
276 	"AMAP",
277 	"reserved 4",
278 	"reserved 5",
279 	"reserved 6",
280 	"reserved 7",
281 };
282 
283 u_int16_t
284 ac97_rdcd(struct ac97_info *codec, int reg)
285 {
286 	return AC97_READ(codec->methods, codec->devinfo, reg);
287 }
288 
289 void
290 ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
291 {
292 	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
293 }
294 
295 static void
296 ac97_reset(struct ac97_info *codec)
297 {
298 	u_int32_t i, ps;
299 	ac97_wrcd(codec, AC97_REG_RESET, 0);
300 	for (i = 0; i < 500; i++) {
301 		ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
302 		if (ps == AC97_POWER_STATUS)
303 			return;
304 		DELAY(1000);
305 	}
306 	device_printf(codec->dev, "AC97 reset timed out.\n");
307 }
308 
309 int
310 ac97_setrate(struct ac97_info *codec, int which, int rate)
311 {
312 	u_int16_t v;
313 
314 	switch(which) {
315 	case AC97_REGEXT_FDACRATE:
316 	case AC97_REGEXT_SDACRATE:
317 	case AC97_REGEXT_LDACRATE:
318 	case AC97_REGEXT_LADCRATE:
319 	case AC97_REGEXT_MADCRATE:
320 		break;
321 
322 	default:
323 		return -1;
324 	}
325 
326 	snd_mtxlock(codec->lock);
327 	if (rate != 0) {
328 		v = rate;
329 		if (codec->extstat & AC97_EXTCAP_DRA)
330 			v >>= 1;
331 		ac97_wrcd(codec, which, v);
332 	}
333 	v = ac97_rdcd(codec, which);
334 	if (codec->extstat & AC97_EXTCAP_DRA)
335 		v <<= 1;
336 	snd_mtxunlock(codec->lock);
337 	return v;
338 }
339 
340 int
341 ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
342 {
343 	mode &= AC97_EXTCAPS;
344 	if ((mode & ~codec->extcaps) != 0) {
345 		device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
346 			      mode);
347 		return -1;
348 	}
349 	snd_mtxlock(codec->lock);
350 	ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
351 	codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
352 	snd_mtxunlock(codec->lock);
353 	return (mode == codec->extstat)? 0 : -1;
354 }
355 
356 u_int16_t
357 ac97_getextmode(struct ac97_info *codec)
358 {
359 	return codec->extstat;
360 }
361 
362 u_int16_t
363 ac97_getextcaps(struct ac97_info *codec)
364 {
365 	return codec->extcaps;
366 }
367 
368 u_int16_t
369 ac97_getcaps(struct ac97_info *codec)
370 {
371 	return codec->caps;
372 }
373 
374 static int
375 ac97_setrecsrc(struct ac97_info *codec, int channel)
376 {
377 	struct ac97mixtable_entry *e = &codec->mix[channel];
378 
379 	if (e->recidx > 0) {
380 		int val = e->recidx - 1;
381 		val |= val << 8;
382 		snd_mtxlock(codec->lock);
383 		ac97_wrcd(codec, AC97_REG_RECSEL, val);
384 		snd_mtxunlock(codec->lock);
385 		return 0;
386 	} else
387 		return -1;
388 }
389 
390 static int
391 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
392 {
393 	struct ac97mixtable_entry *e = &codec->mix[channel];
394 
395 	if (e->reg && e->enable && e->bits) {
396 		int mask, max, val, reg;
397 
398 		reg = (e->reg >= 0) ? e->reg : -e->reg;	/* AC97 register    */
399 		max = (1 << e->bits) - 1;		/* actual range	    */
400 		mask = (max << 8) | max;		/* bits of interest */
401 
402 		if (!e->stereo)
403 			right = left;
404 
405 		/*
406 		 * Invert the range if the polarity requires so,
407 		 * then scale to 0..max-1 to compute the value to
408 		 * write into the codec, and scale back to 0..100
409 		 * for the return value.
410 		 */
411 		if (e->reg > 0) {
412 			left = 100 - left;
413 			right = 100 - right;
414 		}
415 
416 		left = (left * max) / 100;
417 		right = (right * max) / 100;
418 
419 		val = (left << 8) | right;
420 
421 		left = (left * 100) / max;
422 		right = (right * 100) / max;
423 
424 		if (e->reg > 0) {
425 			left = 100 - left;
426 			right = 100 - right;
427 		}
428 
429 		/*
430 		 * For mono controls, trim val and mask, also taking
431 		 * care of e->ofs (offset of control field).
432 		 */
433 		if (e->ofs) {
434 			val &= max;
435 			val <<= e->ofs;
436 			mask = (max << e->ofs);
437 		}
438 
439 		/*
440 		 * If we have a mute bit, add it to the mask and
441 		 * update val and set mute if both channels require a
442 		 * zero volume.
443 		 */
444 		if (e->mute == 1) {
445 			mask |= AC97_MUTE;
446 			if (left == 0 && right == 0)
447 				val = AC97_MUTE;
448 		}
449 
450 		/*
451 		 * If the mask bit is set, do not alter the other bits.
452 		 */
453 		snd_mtxlock(codec->lock);
454 		if (e->mask) {
455 			int cur = ac97_rdcd(codec, e->reg);
456 			val |= cur & ~(mask);
457 		}
458 		ac97_wrcd(codec, reg, val);
459 		snd_mtxunlock(codec->lock);
460 		return left | (right << 8);
461 	} else {
462 		/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
463 		return -1;
464 	}
465 }
466 
467 static void
468 ac97_fix_auxout(struct ac97_info *codec)
469 {
470 	int keep_ogain;
471 
472 	/*
473 	 * By default, The ac97 aux_out register (0x04) corresponds to OSS's
474 	 * OGAIN setting.
475 	 *
476 	 * We first check whether aux_out is a valid register.  If not
477 	 * we may not want to keep ogain.
478 	 */
479 	keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000;
480 
481 	/*
482 	 * Determine what AUX_OUT really means, it can be:
483 	 *
484 	 * 1. Headphone out.
485 	 * 2. 4-Channel Out
486 	 * 3. True line level out (effectively master volume).
487 	 *
488 	 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
489 	 */
490 	if (codec->extcaps & AC97_EXTCAP_SDAC &&
491 	    ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
492 		codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND;
493 		keep_ogain = 1;
494 	}
495 
496 	if (keep_ogain == 0) {
497 		bzero(&codec->mix[SOUND_MIXER_OGAIN],
498 		      sizeof(codec->mix[SOUND_MIXER_OGAIN]));
499 	}
500 }
501 
502 static void
503 ac97_fix_tone(struct ac97_info *codec)
504 {
505 	/* Hide treble and bass if they don't exist */
506 	if ((codec->caps & AC97_CAP_TONE) == 0) {
507 		bzero(&codec->mix[SOUND_MIXER_BASS],
508 		      sizeof(codec->mix[SOUND_MIXER_BASS]));
509 		bzero(&codec->mix[SOUND_MIXER_TREBLE],
510 		      sizeof(codec->mix[SOUND_MIXER_TREBLE]));
511 	}
512 }
513 
514 static const char*
515 ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
516 {
517 	if (cname == NULL) {
518 		sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
519 		return buf;
520 	}
521 
522 	if (vname == NULL) vname = "Unknown";
523 
524 	if (bootverbose) {
525 		sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
526 	} else {
527 		sprintf(buf, "%s %s AC97 Codec", vname, cname);
528 	}
529 	return buf;
530 }
531 
532 static unsigned
533 ac97_initmixer(struct ac97_info *codec)
534 {
535 	ac97_patch codec_patch;
536 	const char *cname, *vname;
537 	char desc[80];
538 	u_int8_t model, step;
539 	unsigned i, j, k, old;
540 	u_int32_t id;
541 
542 	snd_mtxlock(codec->lock);
543 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
544 	if (codec->count == 0) {
545 		device_printf(codec->dev, "ac97 codec init failed\n");
546 		snd_mtxunlock(codec->lock);
547 		return ENODEV;
548 	}
549 
550 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
551 	ac97_reset(codec);
552 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
553 
554 	i = ac97_rdcd(codec, AC97_REG_RESET);
555 	codec->caps = i & 0x03ff;
556 	codec->se =  (i & 0x7c00) >> 10;
557 
558 	id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
559 	if (id == 0 || id == 0xffffffff) {
560 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
561 		snd_mtxunlock(codec->lock);
562 		return ENODEV;
563 	}
564 
565 	codec->id = id;
566 	codec->noext = 0;
567 	codec_patch = NULL;
568 
569 	cname = NULL;
570 	model = step = 0;
571 	for (i = 0; ac97codecid[i].id; i++) {
572 		u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
573 		if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
574 			codec->noext = ac97codecid[i].noext;
575 			codec_patch = ac97codecid[i].patch;
576 			cname = ac97codecid[i].name;
577 			model = (id & modelmask) & 0xff;
578 			step = (id & ~modelmask) & 0xff;
579 			break;
580 		}
581 	}
582 
583 	vname = NULL;
584 	for (i = 0; ac97vendorid[i].id; i++) {
585 		if (ac97vendorid[i].id == (id & 0xffffff00)) {
586 			vname = ac97vendorid[i].name;
587 			break;
588 		}
589 	}
590 
591 	codec->extcaps = 0;
592 	codec->extid = 0;
593 	codec->extstat = 0;
594 	if (!codec->noext) {
595 		i = ac97_rdcd(codec, AC97_REGEXT_ID);
596 		if (i != 0xffff) {
597 			codec->extcaps = i & 0x3fff;
598 			codec->extid =  (i & 0xc000) >> 14;
599 			codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
600 		}
601 	}
602 
603 	for (i = 0; i < 32; i++) {
604 		codec->mix[i] = ac97mixtable_default[i];
605 	}
606 	ac97_fix_auxout(codec);
607 	ac97_fix_tone(codec);
608 	if (codec_patch)
609 		codec_patch(codec);
610 
611 	for (i = 0; i < 32; i++) {
612 		k = codec->noext? codec->mix[i].enable : 1;
613 		if (k && (codec->mix[i].reg > 0)) {
614 			old = ac97_rdcd(codec, codec->mix[i].reg);
615 			ac97_wrcd(codec, codec->mix[i].reg, 0x3f);
616 			j = ac97_rdcd(codec, codec->mix[i].reg);
617 			ac97_wrcd(codec, codec->mix[i].reg, old);
618 			codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
619 			for (k = 1; j & (1 << k); k++);
620 			codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
621 		}
622 		/* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
623 	}
624 
625 	device_printf(codec->dev, "<%s>\n",
626 		      ac97_hw_desc(codec->id, vname, cname, desc));
627 
628 	if (bootverbose) {
629 		device_printf(codec->dev, "Codec features ");
630 		for (i = j = 0; i < 10; i++)
631 			if (codec->caps & (1 << i))
632 				printf("%s%s", j++? ", " : "", ac97feature[i]);
633 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
634 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
635 
636 		if (codec->extcaps != 0 || codec->extid) {
637 			device_printf(codec->dev, "%s codec",
638 				      codec->extid? "Secondary" : "Primary");
639 			if (codec->extcaps)
640 				printf(" extended features ");
641 			for (i = j = 0; i < 14; i++)
642 				if (codec->extcaps & (1 << i))
643 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
644 			printf("\n");
645 		}
646 	}
647 
648 	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
649 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
650 	snd_mtxunlock(codec->lock);
651 	return 0;
652 }
653 
654 static unsigned
655 ac97_reinitmixer(struct ac97_info *codec)
656 {
657 	snd_mtxlock(codec->lock);
658 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
659 	if (codec->count == 0) {
660 		device_printf(codec->dev, "ac97 codec init failed\n");
661 		snd_mtxunlock(codec->lock);
662 		return ENODEV;
663 	}
664 
665 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
666 	ac97_reset(codec);
667 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
668 
669 	if (!codec->noext) {
670 		ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
671 		if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
672 		    != codec->extstat)
673 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
674 				      codec->extstat,
675 				      ac97_rdcd(codec, AC97_REGEXT_STAT) &
676 				      AC97_EXTCAPS);
677 	}
678 
679 	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
680 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
681 	snd_mtxunlock(codec->lock);
682 	return 0;
683 }
684 
685 struct ac97_info *
686 ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
687 {
688 	struct ac97_info *codec;
689 
690 	codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
691 	if (codec == NULL)
692 		return NULL;
693 
694 	snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
695 	codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
696 	codec->methods = kobj_create(cls, M_AC97, M_WAITOK);
697 	if (codec->methods == NULL) {
698 		snd_mtxlock(codec->lock);
699 		snd_mtxfree(codec->lock);
700 		free(codec, M_AC97);
701 		return NULL;
702 	}
703 
704 	codec->dev = dev;
705 	codec->devinfo = devinfo;
706 	codec->flags = 0;
707 	return codec;
708 }
709 
710 void
711 ac97_destroy(struct ac97_info *codec)
712 {
713 	snd_mtxlock(codec->lock);
714 	if (codec->methods != NULL)
715 		kobj_delete(codec->methods, M_AC97);
716 	snd_mtxfree(codec->lock);
717 	free(codec, M_AC97);
718 }
719 
720 void
721 ac97_setflags(struct ac97_info *codec, u_int32_t val)
722 {
723 	codec->flags = val;
724 }
725 
726 u_int32_t
727 ac97_getflags(struct ac97_info *codec)
728 {
729 	return codec->flags;
730 }
731 
732 /* -------------------------------------------------------------------- */
733 
734 static int
735 ac97mix_init(struct snd_mixer *m)
736 {
737 	struct ac97_info *codec = mix_getdevinfo(m);
738 	u_int32_t i, mask;
739 
740 	if (codec == NULL)
741 		return -1;
742 
743 	if (ac97_initmixer(codec))
744 		return -1;
745 
746 	mask = 0;
747 	for (i = 0; i < 32; i++)
748 		mask |= codec->mix[i].enable? 1 << i : 0;
749 	mix_setdevs(m, mask);
750 
751 	mask = 0;
752 	for (i = 0; i < 32; i++)
753 		mask |= codec->mix[i].recidx? 1 << i : 0;
754 	mix_setrecdevs(m, mask);
755 	return 0;
756 }
757 
758 static int
759 ac97mix_uninit(struct snd_mixer *m)
760 {
761 	struct ac97_info *codec = mix_getdevinfo(m);
762 
763 	if (codec == NULL)
764 		return -1;
765 	/*
766 	if (ac97_uninitmixer(codec))
767 		return -1;
768 	*/
769 	ac97_destroy(codec);
770 	return 0;
771 }
772 
773 static int
774 ac97mix_reinit(struct snd_mixer *m)
775 {
776 	struct ac97_info *codec = mix_getdevinfo(m);
777 
778 	if (codec == NULL)
779 		return -1;
780 	return ac97_reinitmixer(codec);
781 }
782 
783 static int
784 ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
785 {
786 	struct ac97_info *codec = mix_getdevinfo(m);
787 
788 	if (codec == NULL)
789 		return -1;
790 	return ac97_setmixer(codec, dev, left, right);
791 }
792 
793 static int
794 ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
795 {
796 	int i;
797 	struct ac97_info *codec = mix_getdevinfo(m);
798 
799 	if (codec == NULL)
800 		return -1;
801 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
802 		if ((src & (1 << i)) != 0)
803 			break;
804 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
805 }
806 
807 static kobj_method_t ac97mixer_methods[] = {
808     	KOBJMETHOD(mixer_init,		ac97mix_init),
809     	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
810     	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
811     	KOBJMETHOD(mixer_set,		ac97mix_set),
812     	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
813 	{ 0, 0 }
814 };
815 MIXER_DECLARE(ac97mixer);
816 
817 /* -------------------------------------------------------------------- */
818 
819 kobj_class_t
820 ac97_getmixerclass(void)
821 {
822 	return &ac97mixer_class;
823 }
824 
825 
826