ac97.c revision 170289
144743Smarkm/*-
244743Smarkm * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
344743Smarkm * All rights reserved.
444743Smarkm *
544743Smarkm * Redistribution and use in source and binary forms, with or without
644743Smarkm * modification, are permitted provided that the following conditions
744743Smarkm * are met:
844743Smarkm * 1. Redistributions of source code must retain the above copyright
944743Smarkm *    notice, this list of conditions and the following disclaimer.
1056977Sshin * 2. Redistributions in binary form must reproduce the above copyright
1156977Sshin *    notice, this list of conditions and the following disclaimer in the
1244743Smarkm *    documentation and/or other materials provided with the distribution.
1344743Smarkm *
1444743Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1544743Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1644743Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1744743Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1844743Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1944743Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2044743Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2144743Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2244743Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2344743Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2444743Smarkm * SUCH DAMAGE.
2544743Smarkm */
2644743Smarkm
2744743Smarkm#include <dev/sound/pcm/sound.h>
2844743Smarkm#include <dev/sound/pcm/ac97.h>
2963152Sdwmalone#include <dev/sound/pcm/ac97_patch.h>
3063152Sdwmalone
3163152Sdwmalone#include <dev/pci/pcivar.h>
3263152Sdwmalone
3344743Smarkm#include "mixer_if.h"
3444743Smarkm
3544743SmarkmSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/ac97.c 170289 2007-06-04 18:25:08Z dwmalone $");
3644743Smarkm
3744743SmarkmMALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
3844743Smarkm
3944743Smarkmstruct ac97mixtable_entry {
4044743Smarkm	int reg;		/* register index		*/
4144743Smarkm				/* reg < 0 if inverted polarity	*/
4244743Smarkm	unsigned bits:4;	/* width of control field	*/
4344743Smarkm	unsigned ofs:4;		/* offset (only if stereo=0)	*/
4444743Smarkm	unsigned stereo:1;	/* set for stereo controls	*/
4544743Smarkm	unsigned mute:1;	/* bit15 is MUTE		*/
4644743Smarkm	unsigned recidx:4;	/* index in rec mux		*/
4744743Smarkm	unsigned mask:1;	/* use only masked bits		*/
4844743Smarkm	unsigned enable:1;	/* entry is enabled		*/
4944743Smarkm};
5044743Smarkm
5144743Smarkm#define AC97_NAMELEN	16
5244743Smarkmstruct ac97_info {
5344743Smarkm	kobj_t methods;
5444743Smarkm	device_t dev;
5544743Smarkm	void *devinfo;
5644743Smarkm	u_int32_t id;
5744743Smarkm	u_int32_t subvendor;
5844743Smarkm	unsigned count, caps, se, extcaps, extid, extstat, noext:1;
5944743Smarkm	u_int32_t flags;
6044743Smarkm	struct ac97mixtable_entry mix[32];
6144743Smarkm	char name[AC97_NAMELEN];
6244743Smarkm	struct mtx *lock;
6344743Smarkm};
6444743Smarkm
6544743Smarkmstruct ac97_vendorid {
6644743Smarkm	u_int32_t   id;
6744743Smarkm	const char *name;
6844743Smarkm};
6944743Smarkm
7044743Smarkmstruct ac97_codecid {
7144743Smarkm	u_int32_t  id;
7244743Smarkm	u_int8_t   stepmask;
7344743Smarkm	u_int8_t   noext:1;
7444743Smarkm	char 	  *name;
7544743Smarkm	ac97_patch patch;
7644743Smarkm};
7756977Sshin
7856977Sshinstatic const struct ac97mixtable_entry ac97mixtable_default[32] = {
7956977Sshin    /*	[offset]			reg	     bits of st mu re mk en */
8056977Sshin	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0, 1 },
8144743Smarkm	[SOUND_MIXER_OGAIN]	= { AC97_MIX_AUXOUT, 	5, 0, 1, 1, 0, 0, 0 },
8244743Smarkm	[SOUND_MIXER_PHONEOUT]	= { AC97_MIX_MONO, 	5, 0, 0, 1, 7, 0, 0 },
8356977Sshin	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1, 0 },
8444743Smarkm	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1, 0 },
8544743Smarkm	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0, 1 },
8644743Smarkm	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0, 0 },
8744743Smarkm	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0, 1 },
8856977Sshin	[SOUND_MIXER_PHONEIN]	= { AC97_MIX_PHONE, 	5, 0, 0, 1, 8, 0, 0 },
8956977Sshin	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 1, 1 },
9056977Sshin	/* use igain for the mic 20dB boost */
9156977Sshin	[SOUND_MIXER_IGAIN] 	= { -AC97_MIX_MIC, 	1, 6, 0, 0, 0, 1, 1 },
9256977Sshin	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
9344743Smarkm	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
9444743Smarkm	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
9556977Sshin	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
9644743Smarkm};
9744743Smarkm
9844743Smarkmstatic const struct ac97_vendorid ac97vendorid[] = {
9944743Smarkm	{ 0x41445300, "Analog Devices" },
10044743Smarkm	{ 0x414b4d00, "Asahi Kasei" },
10144743Smarkm	{ 0x414c4300, "Realtek" },
10256977Sshin	{ 0x414c4700, "Avance Logic" },
10356977Sshin	{ 0x43525900, "Cirrus Logic" },
10456977Sshin	{ 0x434d4900, "C-Media Electronics" },
10556977Sshin	{ 0x43585400, "Conexant" },
10656977Sshin	{ 0x44543000, "Diamond Technology" },
10756977Sshin	{ 0x454d4300, "eMicro" },
10856977Sshin	{ 0x45838300, "ESS Technology" },
10956977Sshin	{ 0x48525300, "Intersil" },
11056977Sshin	{ 0x49434500, "ICEnsemble" },
11156977Sshin	{ 0x49544500, "ITE, Inc." },
11256977Sshin	{ 0x4e534300, "National Semiconductor" },
11356977Sshin	{ 0x50534300, "Philips Semiconductor" },
11456977Sshin	{ 0x83847600, "SigmaTel" },
11556977Sshin	{ 0x53494c00, "Silicon Laboratories" },
11656977Sshin	{ 0x54524100, "TriTech" },
11756977Sshin	{ 0x54584e00, "Texas Instruments" },
11856977Sshin	{ 0x56494100, "VIA Technologies" },
11956977Sshin	{ 0x57454300, "Winbond" },
12056977Sshin	{ 0x574d4c00, "Wolfson" },
12144743Smarkm	{ 0x594d4800, "Yamaha" },
12263152Sdwmalone	/*
12363152Sdwmalone	 * XXX This is a fluke, really! The real vendor
12444743Smarkm	 * should be SigmaTel, not this! This should be
12563152Sdwmalone	 * removed someday!
12663152Sdwmalone	 */
12744743Smarkm	{ 0x01408300, "Creative" },
12844743Smarkm	{ 0x00000000, NULL }
12956977Sshin};
13056977Sshin
13156977Sshinstatic struct ac97_codecid ac97codecid[] = {
13244743Smarkm	{ 0x41445303, 0x00, 0, "AD1819",	0 },
13356977Sshin	{ 0x41445340, 0x00, 0, "AD1881",	0 },
13444743Smarkm	{ 0x41445348, 0x00, 0, "AD1881A",	0 },
13544743Smarkm	{ 0x41445360, 0x00, 0, "AD1885",	0 },
13644743Smarkm	{ 0x41445361, 0x00, 0, "AD1886", 	ad1886_patch },
13744743Smarkm	{ 0x41445362, 0x00, 0, "AD1887", 	0 },
13844743Smarkm	{ 0x41445363, 0x00, 0, "AD1886A", 	0 },
13944743Smarkm	{ 0x41445368, 0x00, 0, "AD1888", 	ad198x_patch },
14044743Smarkm	{ 0x41445370, 0x00, 0, "AD1980",	ad198x_patch },
14144743Smarkm	{ 0x41445372, 0x00, 0, "AD1981A",	0 },
14244743Smarkm	{ 0x41445374, 0x00, 0, "AD1981B",	ad1981b_patch },
14344743Smarkm	{ 0x41445375, 0x00, 0, "AD1985",	ad198x_patch },
14444743Smarkm	{ 0x41445378, 0x00, 0, "AD1986",	ad198x_patch },
14544743Smarkm	{ 0x414b4d00, 0x00, 1, "AK4540", 	0 },
14644743Smarkm	{ 0x414b4d01, 0x00, 1, "AK4542", 	0 },
14744743Smarkm	{ 0x414b4d02, 0x00, 1, "AK4543", 	0 },
14844743Smarkm	{ 0x414b4d06, 0x00, 0, "AK4544A",	0 },
14944743Smarkm	{ 0x454b4d07, 0x00, 0, "AK4545",	0 },
15044743Smarkm	{ 0x414c4320, 0x0f, 0, "ALC100",	0 },
15156977Sshin	{ 0x414c4730, 0x0f, 0, "ALC101",	0 },
15256977Sshin	{ 0x414c4710, 0x0f, 0, "ALC200", 	0 },
15356977Sshin	{ 0x414c4740, 0x0f, 0, "ALC202", 	0 },
15456977Sshin	{ 0x414c4720, 0x0f, 0, "ALC650", 	0 },
15556977Sshin	{ 0x414c4752, 0x0f, 0, "ALC250",	0 },
15656977Sshin	{ 0x414c4760, 0x0f, 0, "ALC655",	alc655_patch },
15756977Sshin	{ 0x414c4770, 0x0f, 0, "ALC203",	0 },
15856977Sshin	{ 0x414c4780, 0x0f, 0, "ALC658",	0 },
15956977Sshin	{ 0x414c4790, 0x0f, 0, "ALC850",	0 },
16056977Sshin	{ 0x43525900, 0x07, 0, "CS4297", 	0 },
16156977Sshin	{ 0x43525910, 0x07, 0, "CS4297A", 	0 },
16256977Sshin	{ 0x43525920, 0x07, 0, "CS4294/98",	0 },
16356977Sshin	{ 0x4352592d, 0x07, 0, "CS4294",	0 },
16456977Sshin	{ 0x43525930, 0x07, 0, "CS4299",	0 },
16556977Sshin	{ 0x43525940, 0x07, 0, "CS4201",	0 },
16656977Sshin	{ 0x43525958, 0x07, 0, "CS4205",	0 },
16756977Sshin	{ 0x43525960, 0x07, 0, "CS4291A",	0 },
16856977Sshin	{ 0x434d4961, 0x00, 0, "CMI9739",	cmi9739_patch },
16956977Sshin	{ 0x434d4941, 0x00, 0, "CMI9738",	0 },
17044743Smarkm	{ 0x434d4978, 0x00, 0, "CMI9761",	0 },
17144743Smarkm	{ 0x434d4982, 0x00, 0, "CMI9761",	0 },
17244743Smarkm	{ 0x434d4983, 0x00, 0, "CMI9761",	0 },
17344743Smarkm	{ 0x43585421, 0x00, 0, "HSD11246",	0 },
17444743Smarkm	{ 0x43585428, 0x07, 0, "CX20468",	0 },
17544743Smarkm	{ 0x43585430, 0x00, 0, "CX20468-21",	0 },
17644743Smarkm	{ 0x44543000, 0x00, 0, "DT0398",	0 },
17744743Smarkm	{ 0x454d4323, 0x00, 0, "EM28023",	0 },
17844743Smarkm	{ 0x454d4328, 0x00, 0, "EM28028",	0 },
17956977Sshin	{ 0x45838308, 0x00, 0, "ES1988",	0 }, /* Formerly ES1921(?) */
18044743Smarkm	{ 0x48525300, 0x00, 0, "HMP9701",	0 },
18144743Smarkm	{ 0x49434501, 0x00, 0, "ICE1230",	0 },
18244743Smarkm	{ 0x49434511, 0x00, 0, "ICE1232",	0 },
18344743Smarkm	{ 0x49434514, 0x00, 0, "ICE1232A",	0 },
18444743Smarkm	{ 0x49434551, 0x03, 0, "VT1616",	0 }, /* Via badged ICE */
18544743Smarkm	{ 0x49544520, 0x00, 0, "ITE2226E",	0 },
18644743Smarkm	{ 0x49544560, 0x07, 0, "ITE2646E",	0 }, /* XXX: patch needed */
18744743Smarkm	{ 0x4e534340, 0x00, 0, "LM4540",	0 }, /* Spec blank on revid */
18856977Sshin	{ 0x4e534343, 0x00, 0, "LM4543",	0 }, /* Ditto */
18956977Sshin	{ 0x4e534346, 0x00, 0, "LM4546A",	0 },
19056977Sshin	{ 0x4e534348, 0x00, 0, "LM4548A",	0 },
19156977Sshin	{ 0x4e534331, 0x00, 0, "LM4549",	0 },
19244743Smarkm	{ 0x4e534349, 0x00, 0, "LM4549A",	0 },
19344743Smarkm	{ 0x4e534350, 0x00, 0, "LM4550",	0 },
19456977Sshin	{ 0x50534301, 0x00, 0, "UCB1510",	0 },
19544743Smarkm	{ 0x50534304, 0x00, 0, "UCB1400",	0 },
19663152Sdwmalone	{ 0x83847600, 0x00, 0, "STAC9700/83/84",	0 },
19744743Smarkm	{ 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
19844743Smarkm	{ 0x83847605, 0x00, 0, "STAC9704",	0 },
19944743Smarkm	{ 0x83847608, 0x00, 0, "STAC9708/11",	0 },
20044743Smarkm	{ 0x83847609, 0x00, 0, "STAC9721/23",	0 },
20144743Smarkm	{ 0x83847644, 0x00, 0, "STAC9744/45",	0 },
20244743Smarkm	{ 0x83847650, 0x00, 0, "STAC9750/51",	0 },
20344743Smarkm	{ 0x83847652, 0x00, 0, "STAC9752/53",	0 },
20444743Smarkm	{ 0x83847656, 0x00, 0, "STAC9756/57",	0 },
20544743Smarkm	{ 0x83847658, 0x00, 0, "STAC9758/59",	0 },
20644743Smarkm	{ 0x83847660, 0x00, 0, "STAC9760/61",	0 }, /* Extrapolated */
20744743Smarkm	{ 0x83847662, 0x00, 0, "STAC9762/63",	0 }, /* Extrapolated */
20844743Smarkm	{ 0x83847666, 0x00, 0, "STAC9766/67",	0 },
20956977Sshin	{ 0x53494c22, 0x00, 0, "Si3036",	0 },
21056977Sshin	{ 0x53494c23, 0x00, 0, "Si3038",	0 },
21156977Sshin	{ 0x54524103, 0x00, 0, "TR28023",	0 }, /* Extrapolated */
21256977Sshin	{ 0x54524106, 0x00, 0, "TR28026",	0 },
21344743Smarkm	{ 0x54524108, 0x00, 0, "TR28028",	0 },
21444743Smarkm	{ 0x54524123, 0x00, 0, "TR28602",	0 },
21556977Sshin	{ 0x54524e03, 0x07, 0, "TLV320AIC27",	0 },
21644743Smarkm	{ 0x54584e20, 0x00, 0, "TLC320AD90",	0 },
21744743Smarkm	{ 0x56494161, 0x00, 0, "VIA1612A",      0 },
21844743Smarkm	{ 0x56494170, 0x00, 0, "VIA1617A",      0 },
21944743Smarkm	{ 0x574d4c00, 0x00, 0, "WM9701A",	0 },
22044743Smarkm	{ 0x574d4c03, 0x00, 0, "WM9703/4/7/8",	0 },
22144743Smarkm	{ 0x574d4c04, 0x00, 0, "WM9704Q",	0 },
22244743Smarkm	{ 0x574d4c05, 0x00, 0, "WM9705/10",	0 },
22344743Smarkm	{ 0x574d4d09, 0x00, 0, "WM9709",	0 },
22444743Smarkm	{ 0x574d4c12, 0x00, 0, "WM9711/12",	0 }, /* XXX: patch needed */
22544743Smarkm	{ 0x57454301, 0x00, 0, "W83971D",	0 },
22644743Smarkm	{ 0x594d4800, 0x00, 0, "YMF743",	0 },
22744743Smarkm	{ 0x594d4802, 0x00, 0, "YMF752",	0 },
22844743Smarkm	{ 0x594d4803, 0x00, 0, "YMF753",	0 },
22944743Smarkm	/*
23044743Smarkm	 * XXX This is a fluke, really! The real codec
23144743Smarkm	 * should be STAC9704, not this! This should be
23244743Smarkm	 * removed someday!
233	 */
234	{ 0x01408384, 0x00, 0, "EV1938",	0 },
235	{ 0, 0, 0, NULL, 0 }
236};
237
238static char *ac97enhancement[] = {
239	"no 3D Stereo Enhancement",
240	"Analog Devices Phat Stereo",
241	"Creative Stereo Enhancement",
242	"National Semi 3D Stereo Enhancement",
243	"Yamaha Ymersion",
244	"BBE 3D Stereo Enhancement",
245	"Crystal Semi 3D Stereo Enhancement",
246	"Qsound QXpander",
247	"Spatializer 3D Stereo Enhancement",
248	"SRS 3D Stereo Enhancement",
249	"Platform Tech 3D Stereo Enhancement",
250	"AKM 3D Audio",
251	"Aureal Stereo Enhancement",
252	"Aztech 3D Enhancement",
253	"Binaura 3D Audio Enhancement",
254	"ESS Technology Stereo Enhancement",
255	"Harman International VMAx",
256	"Nvidea 3D Stereo Enhancement",
257	"Philips Incredible Sound",
258	"Texas Instruments 3D Stereo Enhancement",
259	"VLSI Technology 3D Stereo Enhancement",
260	"TriTech 3D Stereo Enhancement",
261	"Realtek 3D Stereo Enhancement",
262	"Samsung 3D Stereo Enhancement",
263	"Wolfson Microelectronics 3D Enhancement",
264	"Delta Integration 3D Enhancement",
265	"SigmaTel 3D Enhancement",
266	"Reserved 27",
267	"Rockwell 3D Stereo Enhancement",
268	"Reserved 29",
269	"Reserved 30",
270	"Reserved 31"
271};
272
273static char *ac97feature[] = {
274	"mic channel",
275	"reserved",
276	"tone",
277	"simulated stereo",
278	"headphone",
279	"bass boost",
280	"18 bit DAC",
281	"20 bit DAC",
282	"18 bit ADC",
283	"20 bit ADC"
284};
285
286static char *ac97extfeature[] = {
287	"variable rate PCM",
288	"double rate PCM",
289	"reserved 1",
290	"variable rate mic",
291	"reserved 2",
292	"reserved 3",
293	"center DAC",
294	"surround DAC",
295	"LFE DAC",
296	"AMAP",
297	"reserved 4",
298	"reserved 5",
299	"reserved 6",
300	"reserved 7",
301};
302
303u_int16_t
304ac97_rdcd(struct ac97_info *codec, int reg)
305{
306	if (codec->flags & AC97_F_RDCD_BUG) {
307		u_int16_t i[2], j = 100;
308
309		i[0] = AC97_READ(codec->methods, codec->devinfo, reg);
310		i[1] = AC97_READ(codec->methods, codec->devinfo, reg);
311		while (i[0] != i[1] && j)
312			i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg);
313#if 0
314		if (j < 100) {
315			device_printf(codec->dev, "%s(): Inconsistent register value at"
316					" 0x%08x (retry: %d)\n", __func__, reg, 100 - j);
317		}
318#endif
319		return i[!(j & 1)];
320	}
321	return AC97_READ(codec->methods, codec->devinfo, reg);
322}
323
324void
325ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
326{
327	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
328}
329
330static void
331ac97_reset(struct ac97_info *codec)
332{
333	u_int32_t i, ps;
334	ac97_wrcd(codec, AC97_REG_RESET, 0);
335	for (i = 0; i < 500; i++) {
336		ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
337		if (ps == AC97_POWER_STATUS)
338			return;
339		DELAY(1000);
340	}
341	device_printf(codec->dev, "AC97 reset timed out.\n");
342}
343
344int
345ac97_setrate(struct ac97_info *codec, int which, int rate)
346{
347	u_int16_t v;
348
349	switch(which) {
350	case AC97_REGEXT_FDACRATE:
351	case AC97_REGEXT_SDACRATE:
352	case AC97_REGEXT_LDACRATE:
353	case AC97_REGEXT_LADCRATE:
354	case AC97_REGEXT_MADCRATE:
355		break;
356
357	default:
358		return -1;
359	}
360
361	snd_mtxlock(codec->lock);
362	if (rate != 0) {
363		v = rate;
364		if (codec->extstat & AC97_EXTCAP_DRA)
365			v >>= 1;
366		ac97_wrcd(codec, which, v);
367	}
368	v = ac97_rdcd(codec, which);
369	if (codec->extstat & AC97_EXTCAP_DRA)
370		v <<= 1;
371	snd_mtxunlock(codec->lock);
372	return v;
373}
374
375int
376ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
377{
378	mode &= AC97_EXTCAPS;
379	if ((mode & ~codec->extcaps) != 0) {
380		device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
381			      mode);
382		return -1;
383	}
384	snd_mtxlock(codec->lock);
385	ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
386	codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
387	snd_mtxunlock(codec->lock);
388	return (mode == codec->extstat)? 0 : -1;
389}
390
391u_int16_t
392ac97_getextmode(struct ac97_info *codec)
393{
394	return codec->extstat;
395}
396
397u_int16_t
398ac97_getextcaps(struct ac97_info *codec)
399{
400	return codec->extcaps;
401}
402
403u_int16_t
404ac97_getcaps(struct ac97_info *codec)
405{
406	return codec->caps;
407}
408
409u_int32_t
410ac97_getsubvendor(struct ac97_info *codec)
411{
412	return codec->subvendor;
413}
414
415static int
416ac97_setrecsrc(struct ac97_info *codec, int channel)
417{
418	struct ac97mixtable_entry *e = &codec->mix[channel];
419
420	if (e->recidx > 0) {
421		int val = e->recidx - 1;
422		val |= val << 8;
423		snd_mtxlock(codec->lock);
424		ac97_wrcd(codec, AC97_REG_RECSEL, val);
425		snd_mtxunlock(codec->lock);
426		return 0;
427	} else
428		return -1;
429}
430
431static int
432ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
433{
434	struct ac97mixtable_entry *e = &codec->mix[channel];
435
436	if (e->reg && e->enable && e->bits) {
437		int mask, max, val, reg;
438
439		reg = (e->reg >= 0) ? e->reg : -e->reg;	/* AC97 register    */
440		max = (1 << e->bits) - 1;		/* actual range	    */
441		mask = (max << 8) | max;		/* bits of interest */
442
443		if (!e->stereo)
444			right = left;
445
446		/*
447		 * Invert the range if the polarity requires so,
448		 * then scale to 0..max-1 to compute the value to
449		 * write into the codec, and scale back to 0..100
450		 * for the return value.
451		 */
452		if (e->reg > 0) {
453			left = 100 - left;
454			right = 100 - right;
455		}
456
457		left = (left * max) / 100;
458		right = (right * max) / 100;
459
460		val = (left << 8) | right;
461
462		left = (left * 100) / max;
463		right = (right * 100) / max;
464
465		if (e->reg > 0) {
466			left = 100 - left;
467			right = 100 - right;
468		}
469
470		/*
471		 * For mono controls, trim val and mask, also taking
472		 * care of e->ofs (offset of control field).
473		 */
474		if (e->ofs) {
475			val &= max;
476			val <<= e->ofs;
477			mask = (max << e->ofs);
478		}
479
480		/*
481		 * If we have a mute bit, add it to the mask and
482		 * update val and set mute if both channels require a
483		 * zero volume.
484		 */
485		if (e->mute == 1) {
486			mask |= AC97_MUTE;
487			if (left == 0 && right == 0)
488				val = AC97_MUTE;
489		}
490
491		/*
492		 * If the mask bit is set, do not alter the other bits.
493		 */
494		snd_mtxlock(codec->lock);
495		if (e->mask) {
496			int cur = ac97_rdcd(codec, reg);
497			val |= cur & ~(mask);
498		}
499		ac97_wrcd(codec, reg, val);
500		snd_mtxunlock(codec->lock);
501		return left | (right << 8);
502	} else {
503#if 0
504		printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable);
505#endif
506		return -1;
507	}
508}
509
510static void
511ac97_fix_auxout(struct ac97_info *codec)
512{
513	int keep_ogain;
514
515	/*
516	 * By default, The ac97 aux_out register (0x04) corresponds to OSS's
517	 * OGAIN setting.
518	 *
519	 * We first check whether aux_out is a valid register.  If not
520	 * we may not want to keep ogain.
521	 */
522	keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000;
523
524	/*
525	 * Determine what AUX_OUT really means, it can be:
526	 *
527	 * 1. Headphone out.
528	 * 2. 4-Channel Out
529	 * 3. True line level out (effectively master volume).
530	 *
531	 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
532	 */
533	if (codec->extcaps & AC97_EXTCAP_SDAC &&
534	    ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
535		codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND;
536		keep_ogain = 1;
537	}
538
539	if (keep_ogain == 0) {
540		bzero(&codec->mix[SOUND_MIXER_OGAIN],
541		      sizeof(codec->mix[SOUND_MIXER_OGAIN]));
542	}
543}
544
545static void
546ac97_fix_tone(struct ac97_info *codec)
547{
548	/*
549	 * YMF chips does not indicate tone and 3D enhancement capability
550	 * in the AC97_REG_RESET register.
551	 */
552	switch (codec->id) {
553	case 0x594d4800:	/* YMF743 */
554	case 0x594d4803:	/* YMF753 */
555		codec->caps |= AC97_CAP_TONE;
556		codec->se |= 0x04;
557		break;
558	case 0x594d4802:	/* YMF752 */
559		codec->se |= 0x04;
560		break;
561	default:
562		break;
563	}
564
565	/* Hide treble and bass if they don't exist */
566	if ((codec->caps & AC97_CAP_TONE) == 0) {
567		bzero(&codec->mix[SOUND_MIXER_BASS],
568		      sizeof(codec->mix[SOUND_MIXER_BASS]));
569		bzero(&codec->mix[SOUND_MIXER_TREBLE],
570		      sizeof(codec->mix[SOUND_MIXER_TREBLE]));
571	}
572}
573
574static const char*
575ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
576{
577	if (cname == NULL) {
578		sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
579		return buf;
580	}
581
582	if (vname == NULL) vname = "Unknown";
583
584	if (bootverbose) {
585		sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
586	} else {
587		sprintf(buf, "%s %s AC97 Codec", vname, cname);
588	}
589	return buf;
590}
591
592static unsigned
593ac97_initmixer(struct ac97_info *codec)
594{
595	ac97_patch codec_patch;
596	const char *cname, *vname;
597	char desc[80];
598	u_int8_t model, step;
599	unsigned i, j, k, bit, old;
600	u_int32_t id;
601	int reg;
602
603	snd_mtxlock(codec->lock);
604	codec->count = AC97_INIT(codec->methods, codec->devinfo);
605	if (codec->count == 0) {
606		device_printf(codec->dev, "ac97 codec init failed\n");
607		snd_mtxunlock(codec->lock);
608		return ENODEV;
609	}
610
611	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
612	ac97_reset(codec);
613	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
614
615	i = ac97_rdcd(codec, AC97_REG_RESET);
616	j = ac97_rdcd(codec, AC97_REG_RESET);
617	/*
618	 * Let see if this codec can return consistent value.
619	 * If not, turn on aggressive read workaround
620	 * (STAC9704 comes in mind).
621	 */
622	if (i != j) {
623		codec->flags |= AC97_F_RDCD_BUG;
624		i = ac97_rdcd(codec, AC97_REG_RESET);
625	}
626	codec->caps = i & 0x03ff;
627	codec->se =  (i & 0x7c00) >> 10;
628
629	id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
630	if (id == 0 || id == 0xffffffff) {
631		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
632		snd_mtxunlock(codec->lock);
633		return ENODEV;
634	}
635
636	codec->id = id;
637	codec->subvendor = (u_int32_t)pci_get_subdevice(codec->dev) << 16;
638	codec->subvendor |= (u_int32_t)pci_get_subvendor(codec->dev) &
639	    0x0000ffff;
640	codec->noext = 0;
641	codec_patch = NULL;
642
643	cname = NULL;
644	model = step = 0;
645	for (i = 0; ac97codecid[i].id; i++) {
646		u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
647		if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
648			codec->noext = ac97codecid[i].noext;
649			codec_patch = ac97codecid[i].patch;
650			cname = ac97codecid[i].name;
651			model = (id & modelmask) & 0xff;
652			step = (id & ~modelmask) & 0xff;
653			break;
654		}
655	}
656
657	vname = NULL;
658	for (i = 0; ac97vendorid[i].id; i++) {
659		if (ac97vendorid[i].id == (id & 0xffffff00)) {
660			vname = ac97vendorid[i].name;
661			break;
662		}
663	}
664
665	codec->extcaps = 0;
666	codec->extid = 0;
667	codec->extstat = 0;
668	if (!codec->noext) {
669		i = ac97_rdcd(codec, AC97_REGEXT_ID);
670		if (i != 0xffff) {
671			codec->extcaps = i & 0x3fff;
672			codec->extid =  (i & 0xc000) >> 14;
673			codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
674		}
675	}
676
677	for (i = 0; i < 32; i++) {
678		codec->mix[i] = ac97mixtable_default[i];
679	}
680	ac97_fix_auxout(codec);
681	ac97_fix_tone(codec);
682	if (codec_patch)
683		codec_patch(codec);
684
685	for (i = 0; i < 32; i++) {
686		k = codec->noext? codec->mix[i].enable : 1;
687		reg = codec->mix[i].reg;
688		if (reg < 0)
689			reg = -reg;
690		if (k && reg) {
691			j = old = ac97_rdcd(codec, reg);
692			/*
693			 * Test for mute bit (except for AC97_MIX_TONE,
694			 * where we simply assume it as available).
695			 */
696			if (codec->mix[i].mute) {
697				ac97_wrcd(codec, reg, j | 0x8000);
698				j = ac97_rdcd(codec, reg);
699			} else
700				j |= 0x8000;
701			if ((j & 0x8000)) {
702				/*
703				 * Test whether the control width should be
704				 * 4, 5 or 6 bit. For 5bit register, we should
705				 * test it whether it's really 5 or 6bit. Leave
706				 * 4bit register alone, because sometimes an
707				 * attempt to write past 4th bit may cause
708				 * incorrect result especially for AC97_MIX_BEEP
709				 * (ac97 2.3).
710				 */
711				bit = codec->mix[i].bits;
712				if (bit == 5)
713					bit++;
714				j = ((1 << bit) - 1) << codec->mix[i].ofs;
715				ac97_wrcd(codec, reg,
716					j | (codec->mix[i].mute ? 0x8000 : 0));
717				k = ac97_rdcd(codec, reg) & j;
718				k >>= codec->mix[i].ofs;
719				if (reg == AC97_MIX_TONE &&
720							((k & 0x0001) == 0x0000))
721					k >>= 1;
722				for (j = 0; k >> j; j++)
723					;
724				if (j != 0) {
725#if 0
726					device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n",
727						i, k, bit, codec->mix[i].bits, j);
728#endif
729					codec->mix[i].enable = 1;
730					codec->mix[i].bits = j;
731				} else if (reg == AC97_MIX_BEEP) {
732					/*
733					 * Few codec such as CX20468-21 does
734					 * have this control register, although
735					 * the only usable part is the mute bit.
736					 */
737					codec->mix[i].enable = 1;
738				} else
739					codec->mix[i].enable = 0;
740			} else
741				codec->mix[i].enable = 0;
742			ac97_wrcd(codec, reg, old);
743		}
744#if 0
745		printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits);
746#endif
747	}
748
749	device_printf(codec->dev, "<%s>\n",
750		      ac97_hw_desc(codec->id, vname, cname, desc));
751
752	if (bootverbose) {
753		if (codec->flags & AC97_F_RDCD_BUG)
754			device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n");
755		device_printf(codec->dev, "Codec features ");
756		for (i = j = 0; i < 10; i++)
757			if (codec->caps & (1 << i))
758				printf("%s%s", j++? ", " : "", ac97feature[i]);
759		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
760		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
761
762		if (codec->extcaps != 0 || codec->extid) {
763			device_printf(codec->dev, "%s codec",
764				      codec->extid? "Secondary" : "Primary");
765			if (codec->extcaps)
766				printf(" extended features ");
767			for (i = j = 0; i < 14; i++)
768				if (codec->extcaps & (1 << i))
769					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
770			printf("\n");
771		}
772	}
773
774	i = 0;
775	while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) {
776		if (++i == 100) {
777			device_printf(codec->dev, "ac97 codec reports dac not ready\n");
778			break;
779		}
780		DELAY(1000);
781	}
782	if (bootverbose)
783		device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i);
784	snd_mtxunlock(codec->lock);
785	return 0;
786}
787
788static unsigned
789ac97_reinitmixer(struct ac97_info *codec)
790{
791	snd_mtxlock(codec->lock);
792	codec->count = AC97_INIT(codec->methods, codec->devinfo);
793	if (codec->count == 0) {
794		device_printf(codec->dev, "ac97 codec init failed\n");
795		snd_mtxunlock(codec->lock);
796		return ENODEV;
797	}
798
799	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
800	ac97_reset(codec);
801	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
802
803	if (!codec->noext) {
804		ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
805		if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
806		    != codec->extstat)
807			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
808				      codec->extstat,
809				      ac97_rdcd(codec, AC97_REGEXT_STAT) &
810				      AC97_EXTCAPS);
811	}
812
813	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
814		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
815	snd_mtxunlock(codec->lock);
816	return 0;
817}
818
819struct ac97_info *
820ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
821{
822	struct ac97_info *codec;
823	int eapdinv;
824
825	codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT | M_ZERO);
826	if (codec == NULL)
827		return NULL;
828
829	snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
830	codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
831	codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO);
832	codec->dev = dev;
833	codec->devinfo = devinfo;
834	codec->flags = 0;
835	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
836		    "eapdinv", &eapdinv) == 0) {
837		if (eapdinv != 0)
838			codec->flags |= AC97_F_EAPD_INV;
839	}
840	return codec;
841}
842
843void
844ac97_destroy(struct ac97_info *codec)
845{
846	snd_mtxlock(codec->lock);
847	if (codec->methods != NULL)
848		kobj_delete(codec->methods, M_AC97);
849	snd_mtxfree(codec->lock);
850	free(codec, M_AC97);
851}
852
853void
854ac97_setflags(struct ac97_info *codec, u_int32_t val)
855{
856	codec->flags = val;
857}
858
859u_int32_t
860ac97_getflags(struct ac97_info *codec)
861{
862	return codec->flags;
863}
864
865/* -------------------------------------------------------------------- */
866
867#ifdef SND_DYNSYSCTL
868static int
869sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS)
870{
871	struct ac97_info *codec;
872	int ea, inv, err = 0;
873	u_int16_t val;
874
875	codec = oidp->oid_arg1;
876	if (codec == NULL || codec->id == 0 || codec->lock == NULL)
877		return EINVAL;
878	snd_mtxlock(codec->lock);
879	val = ac97_rdcd(codec, AC97_REG_POWER);
880	inv = (codec->flags & AC97_F_EAPD_INV) ? 0 : 1;
881	ea = (val >> 15) ^ inv;
882	snd_mtxunlock(codec->lock);
883	err = sysctl_handle_int(oidp, &ea, 0, req);
884	if (err == 0 && req->newptr != NULL) {
885		if (ea != 0 && ea != 1)
886			return EINVAL;
887		if (ea != ((val >> 15) ^ inv)) {
888			snd_mtxlock(codec->lock);
889			ac97_wrcd(codec, AC97_REG_POWER, val ^ 0x8000);
890			snd_mtxunlock(codec->lock);
891		}
892	}
893	return err;
894}
895#endif
896
897static void
898ac97_init_sysctl(struct ac97_info *codec)
899{
900#ifdef SND_DYNSYSCTL
901	u_int16_t orig, val;
902
903	if (codec == NULL || codec->dev == NULL)
904		return;
905	snd_mtxlock(codec->lock);
906	orig = ac97_rdcd(codec, AC97_REG_POWER);
907	ac97_wrcd(codec, AC97_REG_POWER, orig ^ 0x8000);
908	val = ac97_rdcd(codec, AC97_REG_POWER);
909	ac97_wrcd(codec, AC97_REG_POWER, orig);
910	snd_mtxunlock(codec->lock);
911	if ((val & 0x8000) == (orig & 0x8000))
912		return;
913	SYSCTL_ADD_PROC(device_get_sysctl_ctx(codec->dev),
914	    SYSCTL_CHILDREN(device_get_sysctl_tree(codec->dev)),
915            OID_AUTO, "eapd", CTLTYPE_INT | CTLFLAG_RW,
916	    codec, sizeof(codec), sysctl_hw_snd_ac97_eapd,
917	    "I", "AC97 External Amplifier");
918#endif
919}
920
921static int
922ac97mix_init(struct snd_mixer *m)
923{
924	struct ac97_info *codec = mix_getdevinfo(m);
925	u_int32_t i, mask;
926
927	if (codec == NULL)
928		return -1;
929
930	if (ac97_initmixer(codec))
931		return -1;
932
933	switch (codec->id) {
934	case 0x41445374:	/* AD1981B */
935		if (codec->subvendor == 0x02d91014) {
936			/*
937			 * IBM Thinkcentre:
938			 * Tie "ogain" and "phone" to "vol" since its
939			 * master volume is basically useless and can't
940			 * control anything.
941			 */
942			mask = 0;
943			if (codec->mix[SOUND_MIXER_OGAIN].enable)
944				mask |= SOUND_MASK_OGAIN;
945			if (codec->mix[SOUND_MIXER_PHONEOUT].enable)
946				mask |= SOUND_MASK_PHONEOUT;
947			if (codec->mix[SOUND_MIXER_VOLUME].enable)
948				mix_setparentchild(m, SOUND_MIXER_VOLUME,
949				    mask);
950			else {
951				mix_setparentchild(m, SOUND_MIXER_VOLUME,
952				    mask);
953				mix_setrealdev(m, SOUND_MIXER_VOLUME,
954				    SOUND_MIXER_NONE);
955			}
956		}
957		break;
958	case 0x434d4941:	/* CMI9738 */
959	case 0x434d4961:	/* CMI9739 */
960	case 0x434d4978:	/* CMI9761 */
961	case 0x434d4982:	/* CMI9761 */
962	case 0x434d4983:	/* CMI9761 */
963		ac97_wrcd(codec, AC97_MIX_PCM, 0);
964		bzero(&codec->mix[SOUND_MIXER_PCM],
965		    sizeof(codec->mix[SOUND_MIXER_PCM]));
966		pcm_setflags(codec->dev, pcm_getflags(codec->dev) |
967		    SD_F_SOFTPCMVOL);
968		/* XXX How about master volume ? */
969		break;
970	default:
971		break;
972	}
973
974#if 0
975	/* XXX For the sake of debugging purposes */
976	mix_setparentchild(m, SOUND_MIXER_VOLUME,
977	    SOUND_MASK_PCM | SOUND_MASK_CD);
978	mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
979	ac97_wrcd(codec, AC97_MIX_MASTER, 0);
980#endif
981
982	mask = 0;
983	for (i = 0; i < 32; i++)
984		mask |= codec->mix[i].enable? 1 << i : 0;
985	mix_setdevs(m, mask);
986
987	mask = 0;
988	for (i = 0; i < 32; i++)
989		mask |= codec->mix[i].recidx? 1 << i : 0;
990	mix_setrecdevs(m, mask);
991
992	ac97_init_sysctl(codec);
993
994	return 0;
995}
996
997static int
998ac97mix_uninit(struct snd_mixer *m)
999{
1000	struct ac97_info *codec = mix_getdevinfo(m);
1001
1002	if (codec == NULL)
1003		return -1;
1004	/*
1005	if (ac97_uninitmixer(codec))
1006		return -1;
1007	*/
1008	ac97_destroy(codec);
1009	return 0;
1010}
1011
1012static int
1013ac97mix_reinit(struct snd_mixer *m)
1014{
1015	struct ac97_info *codec = mix_getdevinfo(m);
1016
1017	if (codec == NULL)
1018		return -1;
1019	return ac97_reinitmixer(codec);
1020}
1021
1022static int
1023ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
1024{
1025	struct ac97_info *codec = mix_getdevinfo(m);
1026
1027	if (codec == NULL)
1028		return -1;
1029	return ac97_setmixer(codec, dev, left, right);
1030}
1031
1032static int
1033ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
1034{
1035	int i;
1036	struct ac97_info *codec = mix_getdevinfo(m);
1037
1038	if (codec == NULL)
1039		return -1;
1040	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
1041		if ((src & (1 << i)) != 0)
1042			break;
1043	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
1044}
1045
1046static kobj_method_t ac97mixer_methods[] = {
1047    	KOBJMETHOD(mixer_init,		ac97mix_init),
1048    	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
1049    	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
1050    	KOBJMETHOD(mixer_set,		ac97mix_set),
1051    	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
1052	{ 0, 0 }
1053};
1054MIXER_DECLARE(ac97mixer);
1055
1056/* -------------------------------------------------------------------- */
1057
1058kobj_class_t
1059ac97_getmixerclass(void)
1060{
1061	return &ac97mixer_class;
1062}
1063