ac97.c revision 113788
159243Sobrien/*
259243Sobrien * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
359243Sobrien * All rights reserved.
459243Sobrien *
559243Sobrien * Redistribution and use in source and binary forms, with or without
659243Sobrien * modification, are permitted provided that the following conditions
759243Sobrien * are met:
859243Sobrien * 1. Redistributions of source code must retain the above copyright
959243Sobrien *    notice, this list of conditions and the following disclaimer.
1059243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1159243Sobrien *    notice, this list of conditions and the following disclaimer in the
1259243Sobrien *    documentation and/or other materials provided with the distribution.
1359243Sobrien *
1459243Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1559243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1659243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1759243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1859243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1959243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2059243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2159243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2259243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2359243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2459243Sobrien * SUCH DAMAGE.
2559243Sobrien */
2659243Sobrien
2759243Sobrien#include <dev/sound/pcm/sound.h>
2859243Sobrien#include <dev/sound/pcm/ac97.h>
2959243Sobrien#include <dev/sound/pcm/ac97_patch.h>
3059243Sobrien
3159243Sobrien#include "mixer_if.h"
3259243Sobrien
3359243SobrienSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/ac97.c 113788 2003-04-21 04:48:40Z orion $");
3459243Sobrien
3559243SobrienMALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
3659243Sobrien
3759243Sobrienstruct ac97mixtable_entry {
3859243Sobrien	int	 reg:8;		/* register index		*/
3959243Sobrien				/* reg < 0 if inverted polarity	*/
4059243Sobrien	unsigned bits:4;	/* width of control field	*/
4159243Sobrien	unsigned ofs:4;		/* offset (only if stereo=0)	*/
4259243Sobrien	unsigned stereo:1;	/* set for stereo controls	*/
4359243Sobrien	unsigned mute:1;	/* bit15 is MUTE		*/
4459243Sobrien	unsigned recidx:4;	/* index in rec mux		*/
4559243Sobrien	unsigned mask:1;	/* use only masked bits		*/
4659243Sobrien	unsigned enable:1;	/* entry is enabled		*/
4759243Sobrien};
4859243Sobrien
4959243Sobrien#define AC97_NAMELEN	16
5059243Sobrienstruct ac97_info {
5159243Sobrien	kobj_t methods;
5259243Sobrien	device_t dev;
5359243Sobrien	void *devinfo;
5459243Sobrien	u_int32_t id;
5559243Sobrien	unsigned count, caps, se, extcaps, extid, extstat, noext:1;
5659243Sobrien	u_int32_t flags;
5759243Sobrien	struct ac97mixtable_entry mix[32];
5859243Sobrien	char name[AC97_NAMELEN];
5959243Sobrien	struct mtx *lock;
6059243Sobrien};
6159243Sobrien
6259243Sobrienstruct ac97_vendorid {
6359243Sobrien	u_int32_t   id;
6459243Sobrien	const char *name;
6559243Sobrien};
6659243Sobrien
6759243Sobrienstruct ac97_codecid {
6859243Sobrien	u_int32_t  id;
6959243Sobrien	u_int8_t   stepmask;
7059243Sobrien	u_int8_t   noext:1;
7159243Sobrien	char 	  *name;
7259243Sobrien	ac97_patch patch;
7359243Sobrien};
7459243Sobrien
7559243Sobrienstatic const struct ac97mixtable_entry ac97mixtable_default[32] = {
7659243Sobrien    /*	[offset]			reg	     bits of st mu re mk en */
7759243Sobrien	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0, 1 },
7859243Sobrien	[SOUND_MIXER_MONITOR]	= { AC97_MIX_AUXOUT, 	5, 0, 1, 1, 0, 0, 0 },
7959243Sobrien	[SOUND_MIXER_PHONEOUT]	= { AC97_MIX_MONO, 	5, 0, 0, 1, 7, 0, 0 },
8059243Sobrien	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1, 0 },
8159243Sobrien	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1, 0 },
8259243Sobrien	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0, 1 },
8359243Sobrien	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0, 0 },
8459243Sobrien	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0, 1 },
8559243Sobrien	[SOUND_MIXER_PHONEIN]	= { AC97_MIX_PHONE, 	5, 0, 0, 1, 8, 0, 0 },
8659243Sobrien	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 1, 1 },
8759243Sobrien	/* use igain for the mic 20dB boost */
8859243Sobrien	[SOUND_MIXER_IGAIN] 	= { -AC97_MIX_MIC, 	1, 6, 0, 0, 0, 1, 1 },
8959243Sobrien	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
9059243Sobrien	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
9159243Sobrien	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
9259243Sobrien	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
9359243Sobrien};
9459243Sobrien
9559243Sobrienstatic const struct ac97_vendorid ac97vendorid[] = {
9659243Sobrien	{ 0x41445300, "Analog Devices" },
9759243Sobrien	{ 0x414b4d00, "Asahi Kasei" },
9859243Sobrien	{ 0x414c4300, "Realtek" },
9959243Sobrien	{ 0x414c4700, "Avance Logic" },
10059243Sobrien	{ 0x43525900, "Cirrus Logic" },
10159243Sobrien	{ 0x434d4900, "C-Media Electronics" },
10259243Sobrien	{ 0x43585400, "Conexant" },
10359243Sobrien	{ 0x454d4300, "eMicro" },
10459243Sobrien	{ 0x45838300, "ESS Technology" },
10559243Sobrien	{ 0x49434500, "ICEnsemble" },
10659243Sobrien	{ 0x4e534300, "National Semiconductor" },
10759243Sobrien	{ 0x50534300, "Philips Semiconductor" },
10859243Sobrien	{ 0x83847600, "SigmaTel" },
10959243Sobrien	{ 0x53494c00, "Silicon Laboratory" },
11059243Sobrien	{ 0x54524100, "TriTech" },
11159243Sobrien	{ 0x56494100, "VIA Technologies" },
11259243Sobrien	{ 0x574d4c00, "Wolfson" },
11359243Sobrien	{ 0x594d4800, "Yamaha" },
11459243Sobrien	{ 0x00000000, NULL }
11559243Sobrien};
11659243Sobrien
11759243Sobrienstatic struct ac97_codecid ac97codecid[] = {
11859243Sobrien	{ 0x41445303, 0x00, 0, "AD1819",	0 },
11959243Sobrien	{ 0x41445340, 0x00, 0, "AD1881",	0 },
12059243Sobrien	{ 0x41445348, 0x00, 0, "AD1881A",	0 },
12159243Sobrien	{ 0x41445360, 0x00, 0, "AD1885",	0 },
12259243Sobrien	{ 0x41445361, 0x00, 0, "AD1886", 	ad1886_patch },
12359243Sobrien	{ 0x41445362, 0x00, 0, "AD1887", 	0 },
12459243Sobrien	{ 0x41445363, 0x00, 0, "AD1886A", 	0 },
12559243Sobrien	{ 0x41445370, 0x00, 0, "AD1980",	0 },
12659243Sobrien	{ 0x41445372, 0x00, 0, "AD1981A",	0 },
12759243Sobrien	{ 0x41445374, 0x00, 0, "AD1981B",	0 },
12859243Sobrien	{ 0x41445375, 0x00, 0, "AD1985",	0 },
12959243Sobrien	{ 0x414b4d00, 0x00, 1, "AK4540", 	0 },
13059243Sobrien	{ 0x414b4d01, 0x00, 1, "AK4542", 	0 },
13159243Sobrien	{ 0x414b4d02, 0x00, 1, "AK4543", 	0 },
13259243Sobrien	{ 0x414c4320, 0x0f, 0, "ALC100",	0 },
13359243Sobrien	{ 0x414c4730, 0x0f, 0, "ALC101",	0 },
13459243Sobrien	{ 0x414c4710, 0x0f, 0, "ALC200", 	0 },
13559243Sobrien	{ 0x414c4740, 0x0f, 0, "ALC202", 	0 },
13659243Sobrien	{ 0x414c4720, 0x0f, 0, "ALC650", 	0 },
13759243Sobrien	{ 0x43525900, 0x07, 0, "CS4297", 	0 },
13859243Sobrien	{ 0x43525910, 0x07, 0, "CS4297A", 	0 },
13959243Sobrien	{ 0x43525920, 0x07, 0, "CS4294/98",	0 },
14059243Sobrien	{ 0x43525930, 0x07, 0, "CS4299",	0 },
14159243Sobrien	{ 0x43525940, 0x07, 0, "CS4201",	0 },
14259243Sobrien	{ 0x43525958, 0x07, 0, "CS4205",	0 },
14359243Sobrien	{ 0x43525960, 0x07, 0, "CS4291A",	0 },
14459243Sobrien	{ 0x434d4961, 0x00, 0, "CMI9739",	0 },
14559243Sobrien	{ 0x434d4941, 0x00, 0, "CMI9738",	0 },
14659243Sobrien	{ 0x43585429, 0x00, 0, "CX20468",	0 },
14759243Sobrien	{ 0x454d4323, 0x00, 0, "EM28023",	0 },
14859243Sobrien	{ 0x454d4328, 0x00, 0, "EM28028",	0 },
14959243Sobrien	{ 0x45838308, 0x00, 0, "ES1988",	0 }, /* Formerly ES1921(?) */
15059243Sobrien	{ 0x49434501, 0x00, 0, "ICE1230",	0 },
15159243Sobrien	{ 0x49434511, 0x00, 0, "ICE1232",	0 },
15259243Sobrien	{ 0x49434514, 0x00, 0, "ICE1232A",	0 },
15359243Sobrien	{ 0x49434551, 0x00, 0, "VT1616",	0 }, /* Via badged ICE */
15459243Sobrien	{ 0x4e534340, 0x00, 0, "LM4540",	0 }, /* Spec blank on revid */
15559243Sobrien	{ 0x4e534343, 0x00, 0, "LM4543",	0 }, /* Ditto */
15659243Sobrien	{ 0x4e534346, 0x00, 0, "LM4546A",	0 },
15759243Sobrien	{ 0x4e534348, 0x00, 0, "LM4548A",	0 },
15859243Sobrien	{ 0x4e534331, 0x00, 0, "LM4549",	0 }, /* (?) */
15959243Sobrien	{ 0x4e534349, 0x00, 0, "LM4549A",	0 },
16059243Sobrien	{ 0x4e534350, 0x00, 0, "LM4550",	0 },
16159243Sobrien	{ 0x50534301, 0x00, 0, "UCB1510",	0 },
16259243Sobrien	{ 0x50534304, 0x00, 0, "UCB1400",	0 },
16359243Sobrien	{ 0x83847600, 0x00, 0, "STAC9700/83/84",	0 },
16459243Sobrien	{ 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
16559243Sobrien	{ 0x83847605, 0x00, 0, "STAC9704",	0 },
16659243Sobrien	{ 0x83847608, 0x00, 0, "STAC9708/11",	0 },
16759243Sobrien	{ 0x83847609, 0x00, 0, "STAC9721/23",	0 },
16859243Sobrien	{ 0x83847644, 0x00, 0, "STAC9744/45",	0 },
16959243Sobrien	{ 0x83847650, 0x00, 0, "STAC9750/51",	0 },
17059243Sobrien	{ 0x83847652, 0x00, 0, "STAC9752/53",	0 },
17159243Sobrien	{ 0x83847656, 0x00, 0, "STAC9756/57",	0 },
17259243Sobrien	{ 0x83847658, 0x00, 0, "STAC9758/59",	0 },
17359243Sobrien	{ 0x83847660, 0x00, 0, "STAC9760/61",	0 }, /* Extrapolated */
17459243Sobrien	{ 0x83847662, 0x00, 0, "STAC9762/63",	0 }, /* Extrapolated */
17559243Sobrien	{ 0x53494c22, 0x00, 0, "Si3036",	0 },
17659243Sobrien	{ 0x53494c23, 0x00, 0, "Si3038",	0 },
17759243Sobrien	{ 0x54524103, 0x00, 0, "TR28023",	0 }, /* Extrapolated */
17859243Sobrien	{ 0x54524106, 0x00, 0, "TR28026",	0 },
17959243Sobrien	{ 0x54524108, 0x00, 0, "TR28028",	0 },
18059243Sobrien	{ 0x54524123, 0x00, 0, "TR28602",	0 },
18159243Sobrien	{ 0x56494161, 0x00, 0, "VIA1612A",      0 },
18259243Sobrien	{ 0x574d4c00, 0x00, 0, "WM9701A",	0 },
18359243Sobrien	{ 0x574d4c03, 0x00, 0, "WM9703/4/7/8",	0 },
18459243Sobrien	{ 0x574d4c04, 0x00, 0, "WM9704Q",	0 },
18559243Sobrien	{ 0x574d4c05, 0x00, 0, "WM9705/10",	0 },
18659243Sobrien	{ 0x594d4800, 0x00, 0, "YMF743",	0 },
18759243Sobrien	{ 0x594d4802, 0x00, 0, "YMF752",	0 },
18859243Sobrien	{ 0x594d4803, 0x00, 0, "YMF753",	0 },
18959243Sobrien	{ 0, 0, 0, NULL, 0 }
19059243Sobrien};
19159243Sobrien
19259243Sobrienstatic char *ac97enhancement[] = {
19359243Sobrien	"no 3D Stereo Enhancement",
19459243Sobrien	"Analog Devices Phat Stereo",
19559243Sobrien	"Creative Stereo Enhancement",
19659243Sobrien	"National Semi 3D Stereo Enhancement",
19759243Sobrien	"Yamaha Ymersion",
19859243Sobrien	"BBE 3D Stereo Enhancement",
19959243Sobrien	"Crystal Semi 3D Stereo Enhancement",
20059243Sobrien	"Qsound QXpander",
20159243Sobrien	"Spatializer 3D Stereo Enhancement",
20259243Sobrien	"SRS 3D Stereo Enhancement",
20359243Sobrien	"Platform Tech 3D Stereo Enhancement",
20459243Sobrien	"AKM 3D Audio",
20559243Sobrien	"Aureal Stereo Enhancement",
20659243Sobrien	"Aztech 3D Enhancement",
20759243Sobrien	"Binaura 3D Audio Enhancement",
20859243Sobrien	"ESS Technology Stereo Enhancement",
20959243Sobrien	"Harman International VMAx",
21059243Sobrien	"Nvidea 3D Stereo Enhancement",
21159243Sobrien	"Philips Incredible Sound",
21259243Sobrien	"Texas Instruments 3D Stereo Enhancement",
21359243Sobrien	"VLSI Technology 3D Stereo Enhancement",
21459243Sobrien	"TriTech 3D Stereo Enhancement",
21559243Sobrien	"Realtek 3D Stereo Enhancement",
21659243Sobrien	"Samsung 3D Stereo Enhancement",
21759243Sobrien	"Wolfson Microelectronics 3D Enhancement",
21859243Sobrien	"Delta Integration 3D Enhancement",
21959243Sobrien	"SigmaTel 3D Enhancement",
22059243Sobrien	"Reserved 27",
22159243Sobrien	"Rockwell 3D Stereo Enhancement",
22259243Sobrien	"Reserved 29",
22359243Sobrien	"Reserved 30",
22459243Sobrien	"Reserved 31"
22559243Sobrien};
22659243Sobrien
22759243Sobrienstatic char *ac97feature[] = {
22859243Sobrien	"mic channel",
22959243Sobrien	"reserved",
23059243Sobrien	"tone",
23159243Sobrien	"simulated stereo",
23259243Sobrien	"headphone",
23359243Sobrien	"bass boost",
23459243Sobrien	"18 bit DAC",
23559243Sobrien	"20 bit DAC",
23659243Sobrien	"18 bit ADC",
23759243Sobrien	"20 bit ADC"
23859243Sobrien};
23959243Sobrien
24059243Sobrienstatic char *ac97extfeature[] = {
24159243Sobrien	"variable rate PCM",
24259243Sobrien	"double rate PCM",
24359243Sobrien	"reserved 1",
24459243Sobrien	"variable rate mic",
24559243Sobrien	"reserved 2",
24659243Sobrien	"reserved 3",
24759243Sobrien	"center DAC",
24859243Sobrien	"surround DAC",
24959243Sobrien	"LFE DAC",
25059243Sobrien	"AMAP",
25159243Sobrien	"reserved 4",
25259243Sobrien	"reserved 5",
25359243Sobrien	"reserved 6",
25459243Sobrien	"reserved 7",
25559243Sobrien};
25659243Sobrien
25759243Sobrienu_int16_t
25859243Sobrienac97_rdcd(struct ac97_info *codec, int reg)
25959243Sobrien{
26059243Sobrien	return AC97_READ(codec->methods, codec->devinfo, reg);
26159243Sobrien}
26259243Sobrien
26359243Sobrienvoid
26459243Sobrienac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
26559243Sobrien{
26659243Sobrien	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
26759243Sobrien}
26859243Sobrien
26959243Sobrienstatic void
27059243Sobrienac97_reset(struct ac97_info *codec)
27159243Sobrien{
27259243Sobrien	u_int32_t i, ps;
27359243Sobrien	ac97_wrcd(codec, AC97_REG_RESET, 0);
27459243Sobrien	for (i = 0; i < 500; i++) {
27559243Sobrien		ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
27659243Sobrien		if (ps == AC97_POWER_STATUS)
27759243Sobrien			return;
27859243Sobrien		DELAY(1000);
27959243Sobrien	}
28059243Sobrien	device_printf(codec->dev, "AC97 reset timed out.\n");
28159243Sobrien}
28259243Sobrien
28359243Sobrienint
28459243Sobrienac97_setrate(struct ac97_info *codec, int which, int rate)
28559243Sobrien{
28659243Sobrien	u_int16_t v;
28759243Sobrien
28859243Sobrien	switch(which) {
289	case AC97_REGEXT_FDACRATE:
290	case AC97_REGEXT_SDACRATE:
291	case AC97_REGEXT_LDACRATE:
292	case AC97_REGEXT_LADCRATE:
293	case AC97_REGEXT_MADCRATE:
294		break;
295
296	default:
297		return -1;
298	}
299
300	snd_mtxlock(codec->lock);
301	if (rate != 0) {
302		v = rate;
303		if (codec->extstat & AC97_EXTCAP_DRA)
304			v >>= 1;
305		ac97_wrcd(codec, which, v);
306	}
307	v = ac97_rdcd(codec, which);
308	if (codec->extstat & AC97_EXTCAP_DRA)
309		v <<= 1;
310	snd_mtxunlock(codec->lock);
311	return v;
312}
313
314int
315ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
316{
317	mode &= AC97_EXTCAPS;
318	if ((mode & ~codec->extcaps) != 0) {
319		device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
320			      mode);
321		return -1;
322	}
323	snd_mtxlock(codec->lock);
324	ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
325	codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
326	snd_mtxunlock(codec->lock);
327	return (mode == codec->extstat)? 0 : -1;
328}
329
330u_int16_t
331ac97_getextmode(struct ac97_info *codec)
332{
333	return codec->extstat;
334}
335
336u_int16_t
337ac97_getextcaps(struct ac97_info *codec)
338{
339	return codec->extcaps;
340}
341
342u_int16_t
343ac97_getcaps(struct ac97_info *codec)
344{
345	return codec->caps;
346}
347
348static int
349ac97_setrecsrc(struct ac97_info *codec, int channel)
350{
351	struct ac97mixtable_entry *e = &codec->mix[channel];
352
353	if (e->recidx > 0) {
354		int val = e->recidx - 1;
355		val |= val << 8;
356		snd_mtxlock(codec->lock);
357		ac97_wrcd(codec, AC97_REG_RECSEL, val);
358		snd_mtxunlock(codec->lock);
359		return 0;
360	} else
361		return -1;
362}
363
364static int
365ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
366{
367	struct ac97mixtable_entry *e = &codec->mix[channel];
368
369	if (e->reg && e->enable && e->bits) {
370		int mask, max, val, reg;
371
372		reg = (e->reg >= 0) ? e->reg : -e->reg;	/* AC97 register    */
373		max = (1 << e->bits) - 1;		/* actual range	    */
374		mask = (max << 8) | max;		/* bits of interest */
375
376		if (!e->stereo)
377			right = left;
378
379		/*
380		 * Invert the range if the polarity requires so,
381		 * then scale to 0..max-1 to compute the value to
382		 * write into the codec, and scale back to 0..100
383		 * for the return value.
384		 */
385		if (e->reg > 0) {
386			left = 100 - left;
387			right = 100 - right;
388		}
389
390		left = (left * max) / 100;
391		right = (right * max) / 100;
392
393		val = (left << 8) | right;
394
395		left = (left * 100) / max;
396		right = (right * 100) / max;
397
398		if (e->reg > 0) {
399			left = 100 - left;
400			right = 100 - right;
401		}
402
403		/*
404		 * For mono controls, trim val and mask, also taking
405		 * care of e->ofs (offset of control field).
406		 */
407		if (e->ofs) {
408			val &= max;
409			val <<= e->ofs;
410			mask = (max << e->ofs);
411		}
412
413		/*
414		 * If we have a mute bit, add it to the mask and
415		 * update val and set mute if both channels require a
416		 * zero volume.
417		 */
418		if (e->mute == 1) {
419			mask |= AC97_MUTE;
420			if (left == 0 && right == 0)
421				val = AC97_MUTE;
422		}
423
424		/*
425		 * If the mask bit is set, do not alter the other bits.
426		 */
427		snd_mtxlock(codec->lock);
428		if (e->mask) {
429			int cur = ac97_rdcd(codec, e->reg);
430			val |= cur & ~(mask);
431		}
432		ac97_wrcd(codec, reg, val);
433		snd_mtxunlock(codec->lock);
434		return left | (right << 8);
435	} else {
436		/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
437		return -1;
438	}
439}
440
441#if 0
442static int
443ac97_getmixer(struct ac97_info *codec, int channel)
444{
445	struct ac97mixtable_entry *e = &codec->mix[channel];
446	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
447		int max, val, volume;
448
449		max = (1 << e->bits) - 1;
450		val = ac97_rdcd(code, e->reg);
451		if (val == AC97_MUTE && e->mute == 1)
452			volume = 0;
453		else {
454			if (e->stereo == 0) val >>= e->ofs;
455			val &= max;
456			volume = (val * 100) / max;
457			if (e->reg > 0) volume = 100 - volume;
458		}
459		return volume;
460	} else
461		return -1;
462}
463#endif
464
465static void
466ac97_fix_auxout(struct ac97_info *codec)
467{
468	/* Determine what AUXOUT really means, it can be:
469	 *
470	 * 1. Headphone out.
471	 * 2. 4-Channel Out
472	 * 3. True line level out (effectively master volume).
473	 *
474	 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
475	 */
476	if (codec->caps & AC97_CAP_HEADPHONE) {
477		/* XXX We should probably check the AUX_OUT initial value.
478		 * Leave AC97_MIX_AUXOUT - SOUND_MIXER_MONITOR relationship */
479		return;
480	} else if (codec->extcaps & AC97_EXTCAP_SDAC &&
481		   ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
482		/* 4-Channel Out, add an additional gain setting. */
483		codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR];
484	} else {
485		/* Master volume is/maybe fixed in h/w, not sufficiently
486		 * clear in spec to blat SOUND_MIXER_MASTER. */
487		codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR];
488	}
489	/* Blat monitor, inappropriate label if we get here */
490	bzero(&codec->mix[SOUND_MIXER_MONITOR],
491	      sizeof(codec->mix[SOUND_MIXER_MONITOR]));
492}
493
494static const char*
495ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
496{
497	if (cname == NULL) {
498		sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
499		return buf;
500	}
501
502	if (vname == NULL) vname = "Unknown";
503
504	if (bootverbose) {
505		sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
506	} else {
507		sprintf(buf, "%s %s AC97 Codec", vname, cname);
508	}
509	return buf;
510}
511
512static unsigned
513ac97_initmixer(struct ac97_info *codec)
514{
515	ac97_patch codec_patch;
516	const char *cname, *vname;
517	char desc[80];
518	u_int8_t model, step;
519	unsigned i, j, k, old;
520	u_int32_t id;
521
522	snd_mtxlock(codec->lock);
523	codec->count = AC97_INIT(codec->methods, codec->devinfo);
524	if (codec->count == 0) {
525		device_printf(codec->dev, "ac97 codec init failed\n");
526		snd_mtxunlock(codec->lock);
527		return ENODEV;
528	}
529
530	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
531	ac97_reset(codec);
532	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
533
534	i = ac97_rdcd(codec, AC97_REG_RESET);
535	codec->caps = i & 0x03ff;
536	codec->se =  (i & 0x7c00) >> 10;
537
538	id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
539	if (id == 0 || id == 0xffffffff) {
540		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
541		snd_mtxunlock(codec->lock);
542		return ENODEV;
543	}
544
545	codec->id = id;
546	codec->noext = 0;
547	codec_patch = NULL;
548
549	cname = NULL;
550	model = step = 0;
551	for (i = 0; ac97codecid[i].id; i++) {
552		u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
553		if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
554			codec->noext = ac97codecid[i].noext;
555			codec_patch = ac97codecid[i].patch;
556			cname = ac97codecid[i].name;
557			model = (id & modelmask) & 0xff;
558			step = (id & ~modelmask) & 0xff;
559			break;
560		}
561	}
562
563	vname = NULL;
564	for (i = 0; ac97vendorid[i].id; i++) {
565		if (ac97vendorid[i].id == (id & 0xffffff00)) {
566			vname = ac97vendorid[i].name;
567			break;
568		}
569	}
570
571	codec->extcaps = 0;
572	codec->extid = 0;
573	codec->extstat = 0;
574	if (!codec->noext) {
575		i = ac97_rdcd(codec, AC97_REGEXT_ID);
576		if (i != 0xffff) {
577			codec->extcaps = i & 0x3fff;
578			codec->extid =  (i & 0xc000) >> 14;
579			codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
580		}
581	}
582
583	for (i = 0; i < 32; i++) {
584		codec->mix[i] = ac97mixtable_default[i];
585	}
586	ac97_fix_auxout(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