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