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