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