xref: /freebsd/sys/dev/sound/pcm/ac97.c (revision ba548c643ae0e07aa6141242f25c14b44dcaf3bd)
1987e5972SCameron Grant /*
2987e5972SCameron Grant  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3987e5972SCameron Grant  * All rights reserved.
4987e5972SCameron Grant  *
5987e5972SCameron Grant  * Redistribution and use in source and binary forms, with or without
6987e5972SCameron Grant  * modification, are permitted provided that the following conditions
7987e5972SCameron Grant  * are met:
8987e5972SCameron Grant  * 1. Redistributions of source code must retain the above copyright
9987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer.
10987e5972SCameron Grant  * 2. Redistributions in binary form must reproduce the above copyright
11987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer in the
12987e5972SCameron Grant  *    documentation and/or other materials provided with the distribution.
13987e5972SCameron Grant  *
14987e5972SCameron Grant  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15987e5972SCameron Grant  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16987e5972SCameron Grant  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17987e5972SCameron Grant  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18987e5972SCameron Grant  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19987e5972SCameron Grant  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20987e5972SCameron Grant  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21987e5972SCameron Grant  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22987e5972SCameron Grant  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23987e5972SCameron Grant  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24987e5972SCameron Grant  * SUCH DAMAGE.
25987e5972SCameron Grant  */
26987e5972SCameron Grant 
27ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
28ef9308b1SCameron Grant #include <dev/sound/pcm/ac97.h>
29f9eb1409SOrion Hodson #include <dev/sound/pcm/ac97_patch.h>
30987e5972SCameron Grant 
310f55ac6cSCameron Grant #include "mixer_if.h"
32987e5972SCameron Grant 
3367b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$");
3467b1dce3SCameron Grant 
350f55ac6cSCameron Grant MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
36987e5972SCameron Grant 
3779bb7d52SCameron Grant struct ac97mixtable_entry {
3879bb7d52SCameron Grant 	int		reg:8;
3979bb7d52SCameron Grant 	unsigned	bits:4;
4079bb7d52SCameron Grant 	unsigned	ofs:4;
4179bb7d52SCameron Grant 	unsigned	stereo:1;
4279bb7d52SCameron Grant 	unsigned	mute:1;
4379bb7d52SCameron Grant 	unsigned	recidx:4;
4479bb7d52SCameron Grant 	unsigned        mask:1;
4579bb7d52SCameron Grant 	unsigned	enable:1;
4679bb7d52SCameron Grant };
4779bb7d52SCameron Grant 
4866ef8af5SCameron Grant #define AC97_NAMELEN	16
4966ef8af5SCameron Grant struct ac97_info {
5066ef8af5SCameron Grant 	kobj_t methods;
5166ef8af5SCameron Grant 	device_t dev;
5266ef8af5SCameron Grant 	void *devinfo;
5319921b23SOrion Hodson 	u_int32_t id;
5466ef8af5SCameron Grant 	unsigned count, caps, se, extcaps, extid, extstat, noext:1;
5579bb7d52SCameron Grant 	u_int32_t flags;
5666ef8af5SCameron Grant 	struct ac97mixtable_entry mix[32];
5766ef8af5SCameron Grant 	char name[AC97_NAMELEN];
5800acb133SCameron Grant 	struct mtx *lock;
5966ef8af5SCameron Grant };
6066ef8af5SCameron Grant 
6119921b23SOrion Hodson struct ac97_vendorid {
6219921b23SOrion Hodson 	u_int32_t   id;
6319921b23SOrion Hodson 	const char *name;
6419921b23SOrion Hodson };
6519921b23SOrion Hodson 
66987e5972SCameron Grant struct ac97_codecid {
6719921b23SOrion Hodson 	u_int32_t  id;
6819921b23SOrion Hodson 	u_int8_t   stepmask;
6919921b23SOrion Hodson 	u_int8_t   noext:1;
70987e5972SCameron Grant 	char 	  *name;
71f9eb1409SOrion Hodson 	ac97_patch patch;
72987e5972SCameron Grant };
73987e5972SCameron Grant 
74987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = {
75341f16ccSCameron Grant 	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0, 1 },
76108082c4SOrion Hodson 	[SOUND_MIXER_MONITOR]	= { AC97_MIX_AUXOUT, 	5, 0, 1, 1, 0, 0, 0 },
77341f16ccSCameron Grant 	[SOUND_MIXER_PHONEOUT]	= { AC97_MIX_MONO, 	5, 0, 0, 1, 7, 0, 0 },
78341f16ccSCameron Grant 	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1, 0 },
79341f16ccSCameron Grant 	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1, 0 },
80341f16ccSCameron Grant 	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0, 1 },
81341f16ccSCameron Grant 	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0, 0 },
82341f16ccSCameron Grant 	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0, 1 },
83341f16ccSCameron Grant 	[SOUND_MIXER_PHONEIN]	= { AC97_MIX_PHONE, 	5, 0, 0, 1, 8, 0, 0 },
84341f16ccSCameron Grant 	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 0, 1 },
85341f16ccSCameron Grant 	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
86341f16ccSCameron Grant 	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
87341f16ccSCameron Grant 	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
88341f16ccSCameron Grant 	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
89987e5972SCameron Grant };
90987e5972SCameron Grant 
9119921b23SOrion Hodson static const struct ac97_vendorid ac97vendorid[] = {
9219921b23SOrion Hodson 	{ 0x41445300, "Analog Devices" },
9319921b23SOrion Hodson 	{ 0x414b4d00, "Asahi Kasei" },
9419921b23SOrion Hodson 	{ 0x414c4300, "Realtek" },
9519921b23SOrion Hodson 	{ 0x414c4700, "Avance Logic" },
9619921b23SOrion Hodson 	{ 0x43525900, "Cirrus Logic" },
9719921b23SOrion Hodson 	{ 0x434d4900, "C-Media Electronics" },
9819921b23SOrion Hodson 	{ 0x43585400, "Conexant" },
9919921b23SOrion Hodson 	{ 0x45838300, "ESS Technology" },
10019921b23SOrion Hodson 	{ 0x49434500, "ICEnsemble" },
10119921b23SOrion Hodson 	{ 0x4e534300, "National Semiconductor" },
10219921b23SOrion Hodson 	{ 0x50534300, "Philips Semiconductor" },
10319921b23SOrion Hodson 	{ 0x83847600, "SigmaTel" },
10419921b23SOrion Hodson 	{ 0x53494c00, "Silicon Laboratory" },
10519921b23SOrion Hodson 	{ 0x54524100, "TriTech" },
10619921b23SOrion Hodson 	{ 0x56494100, "VIA Technologies" },
10719921b23SOrion Hodson 	{ 0x574d4c00, "Wolfson" },
10819921b23SOrion Hodson 	{ 0x594d4800, "Yamaha" },
10919921b23SOrion Hodson 	{ 0x00000000, NULL }
11019921b23SOrion Hodson };
11119921b23SOrion Hodson 
112987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = {
11319921b23SOrion Hodson 	{ 0x41445303, 0x00, 0, "AD1819",	0 },
11419921b23SOrion Hodson 	{ 0x41445340, 0x00, 0, "AD1881",	0 },
11519921b23SOrion Hodson 	{ 0x41445348, 0x00, 0, "AD1881A",	0 },
11619921b23SOrion Hodson 	{ 0x41445360, 0x00, 0, "AD1885",	0 },
11719921b23SOrion Hodson 	{ 0x41445361, 0x00, 0, "AD1886", 	ad1886_patch },
118ba548c64SOrion Hodson 	{ 0x41445362, 0x00, 0, "AD1887", 	0 },
119ba548c64SOrion Hodson 	{ 0x41445363, 0x00, 0, "AD1886A", 	0 },
120ba548c64SOrion Hodson 	{ 0x41445370, 0x00, 0, "AD1980",	0 },
121ba548c64SOrion Hodson 	{ 0x41445372, 0x00, 0, "AD1981A",	0 },
122ba548c64SOrion Hodson 	{ 0x41445374, 0x00, 0, "AD1981B",	0 },
123ba548c64SOrion Hodson 	{ 0x41445375, 0x00, 0, "AD1985",	0 },
12419921b23SOrion Hodson 	{ 0x414b4d00, 0x00, 1, "AK4540", 	0 },
12519921b23SOrion Hodson 	{ 0x414b4d01, 0x00, 1, "AK4542", 	0 },
12619921b23SOrion Hodson 	{ 0x414b4d02, 0x00, 1, "AK4543", 	0 },
12719921b23SOrion Hodson 	{ 0x414c4320, 0x0f, 0, "ALC100",	0 },
1289963235aSOrion Hodson 	{ 0x414c4730, 0x0f, 0, "ALC101",	0 },
12919921b23SOrion Hodson 	{ 0x414c4710, 0x0f, 0, "ALC200", 	0 },
13019921b23SOrion Hodson 	{ 0x414c4740, 0x0f, 0, "ALC202", 	0 },
13119921b23SOrion Hodson 	{ 0x414c4720, 0x0f, 0, "ALC650", 	0 },
13219921b23SOrion Hodson 	{ 0x43525900, 0x07, 0, "CS4297", 	0 },
13319921b23SOrion Hodson 	{ 0x43525910, 0x07, 0, "CS4297A", 	0 },
13419921b23SOrion Hodson 	{ 0x43525920, 0x07, 0, "CS4294/98",	0 },
13519921b23SOrion Hodson 	{ 0x43525930, 0x07, 0, "CS4299",	0 },
13619921b23SOrion Hodson 	{ 0x43525940, 0x07, 0, "CS4201",	0 },
137b2a0f525SOrion Hodson 	{ 0x43525958, 0x07, 0, "CS4205",	0 },
13819921b23SOrion Hodson 	{ 0x43525960, 0x07, 0, "CS4291A",	0 },
13919921b23SOrion Hodson 	{ 0x434d4961, 0x00, 0, "CMI9739",	0 },
14019921b23SOrion Hodson 	{ 0x434d4941, 0x00, 0, "CMI9738",	0 },
14119921b23SOrion Hodson 	{ 0x43585429, 0x00, 0, "CX20468",	0 },
14219921b23SOrion Hodson 	{ 0x45838308, 0x00, 0, "ES1988",	0 }, /* Formerly ES1921(?) */
14319921b23SOrion Hodson 	{ 0x49434501, 0x00, 0, "ICE1230",	0 },
14419921b23SOrion Hodson 	{ 0x49434511, 0x00, 0, "ICE1232",	0 },
14519921b23SOrion Hodson 	{ 0x49434514, 0x00, 0, "ICE1232A",	0 },
14619921b23SOrion Hodson 	{ 0x49434551, 0x00, 0, "VT1616",	0 }, /* Via badged ICE */
14719921b23SOrion Hodson 	{ 0x4e534340, 0x00, 0, "LM4540",	0 }, /* Spec blank on revid */
14819921b23SOrion Hodson 	{ 0x4e534343, 0x00, 0, "LM4543",	0 }, /* Ditto */
14919921b23SOrion Hodson 	{ 0x4e534346, 0x00, 0, "LM4546A",	0 },
15019921b23SOrion Hodson 	{ 0x4e534348, 0x00, 0, "LM4548A",	0 },
15119921b23SOrion Hodson 	{ 0x4e534331, 0x00, 0, "LM4549",	0 }, /* (?) */
15219921b23SOrion Hodson 	{ 0x4e534349, 0x00, 0, "LM4549A",	0 },
15319921b23SOrion Hodson 	{ 0x4e534350, 0x00, 0, "LM4550",	0 },
15419921b23SOrion Hodson 	{ 0x50534301, 0x00, 0, "UCB1510",	0 },
15519921b23SOrion Hodson 	{ 0x50534304, 0x00, 0, "UCB1400",	0 },
15619921b23SOrion Hodson 	{ 0x83847600, 0x00, 0, "STAC9700/83/84",	0 },
15719921b23SOrion Hodson 	{ 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
15819921b23SOrion Hodson 	{ 0x83847605, 0x00, 0, "STAC9704",		0 },
15919921b23SOrion Hodson 	{ 0x83847608, 0x00, 0, "STAC9708/11",	0 },
16019921b23SOrion Hodson 	{ 0x83847609, 0x00, 0, "STAC9721/23",	0 },
16119921b23SOrion Hodson 	{ 0x83847644, 0x00, 0, "STAC9744/45",	0 },
16219921b23SOrion Hodson 	{ 0x83847650, 0x00, 0, "STAC9750/51",	0 },
16319921b23SOrion Hodson 	{ 0x83847652, 0x00, 0, "STAC9752/53",	0 },
16419921b23SOrion Hodson 	{ 0x83847656, 0x00, 0, "STAC9756/57",	0 },
16519921b23SOrion Hodson 	{ 0x83847658, 0x00, 0, "STAC9758/59",	0 },
16619921b23SOrion Hodson 	{ 0x83847660, 0x00, 0, "STAC9760/61",	0 }, /* Extrapolated */
16719921b23SOrion Hodson 	{ 0x83847662, 0x00, 0, "STAC9762/63",	0 }, /* Extrapolated */
16819921b23SOrion Hodson 	{ 0x53494c22, 0x00, 0, "Si3036",	0 },
16919921b23SOrion Hodson 	{ 0x53494c23, 0x00, 0, "Si3038",	0 },
17019921b23SOrion Hodson 	{ 0x54524103, 0x00, 0, "TR28023",	0 }, /* Extrapolated */
17119921b23SOrion Hodson 	{ 0x54524106, 0x00, 0, "TR28026",	0 },
17219921b23SOrion Hodson 	{ 0x54524108, 0x00, 0, "TR28028",	0 },
17319921b23SOrion Hodson 	{ 0x54524123, 0x00, 0, "TR28602",	0 },
17419921b23SOrion Hodson 	{ 0x56494161, 0x00, 0, "VIA1612A",      0 },
17519921b23SOrion Hodson 	{ 0x574d4c00, 0x00, 0, "WM9701A",	0 },
17619921b23SOrion Hodson 	{ 0x574d4c03, 0x00, 0, "WM9703/4/7/8",	0 },
17719921b23SOrion Hodson 	{ 0x574d4c04, 0x00, 0, "WM9704Q",	0 },
17819921b23SOrion Hodson 	{ 0x574d4c05, 0x00, 0, "WM9705/10",	0 },
17919921b23SOrion Hodson 	{ 0x594d4800, 0x00, 0, "YMF743",	0 },
18019921b23SOrion Hodson 	{ 0x594d4802, 0x00, 0, "YMF752",	0 },
18119921b23SOrion Hodson 	{ 0x594d4803, 0x00, 0, "YMF753",	0 },
18219921b23SOrion Hodson 	{ 0, 0, 0, NULL, 0 }
183987e5972SCameron Grant };
184987e5972SCameron Grant 
185987e5972SCameron Grant static char *ac97enhancement[] = {
18604553e63SCameron Grant 	"no 3D Stereo Enhancement",
187987e5972SCameron Grant 	"Analog Devices Phat Stereo",
188987e5972SCameron Grant 	"Creative Stereo Enhancement",
189987e5972SCameron Grant 	"National Semi 3D Stereo Enhancement",
190987e5972SCameron Grant 	"Yamaha Ymersion",
191987e5972SCameron Grant 	"BBE 3D Stereo Enhancement",
192987e5972SCameron Grant 	"Crystal Semi 3D Stereo Enhancement",
193987e5972SCameron Grant 	"Qsound QXpander",
194987e5972SCameron Grant 	"Spatializer 3D Stereo Enhancement",
195987e5972SCameron Grant 	"SRS 3D Stereo Enhancement",
196987e5972SCameron Grant 	"Platform Tech 3D Stereo Enhancement",
197987e5972SCameron Grant 	"AKM 3D Audio",
198987e5972SCameron Grant 	"Aureal Stereo Enhancement",
199987e5972SCameron Grant 	"Aztech 3D Enhancement",
200987e5972SCameron Grant 	"Binaura 3D Audio Enhancement",
201987e5972SCameron Grant 	"ESS Technology Stereo Enhancement",
202987e5972SCameron Grant 	"Harman International VMAx",
203987e5972SCameron Grant 	"Nvidea 3D Stereo Enhancement",
204987e5972SCameron Grant 	"Philips Incredible Sound",
205987e5972SCameron Grant 	"Texas Instruments 3D Stereo Enhancement",
206987e5972SCameron Grant 	"VLSI Technology 3D Stereo Enhancement",
207987e5972SCameron Grant 	"TriTech 3D Stereo Enhancement",
208987e5972SCameron Grant 	"Realtek 3D Stereo Enhancement",
209987e5972SCameron Grant 	"Samsung 3D Stereo Enhancement",
210987e5972SCameron Grant 	"Wolfson Microelectronics 3D Enhancement",
211987e5972SCameron Grant 	"Delta Integration 3D Enhancement",
212987e5972SCameron Grant 	"SigmaTel 3D Enhancement",
213987e5972SCameron Grant 	"Reserved 27",
214987e5972SCameron Grant 	"Rockwell 3D Stereo Enhancement",
215987e5972SCameron Grant 	"Reserved 29",
216987e5972SCameron Grant 	"Reserved 30",
217987e5972SCameron Grant 	"Reserved 31"
218987e5972SCameron Grant };
219987e5972SCameron Grant 
220987e5972SCameron Grant static char *ac97feature[] = {
221987e5972SCameron Grant 	"mic channel",
222987e5972SCameron Grant 	"reserved",
223987e5972SCameron Grant 	"tone",
224987e5972SCameron Grant 	"simulated stereo",
225987e5972SCameron Grant 	"headphone",
226987e5972SCameron Grant 	"bass boost",
227987e5972SCameron Grant 	"18 bit DAC",
228987e5972SCameron Grant 	"20 bit DAC",
229987e5972SCameron Grant 	"18 bit ADC",
230987e5972SCameron Grant 	"20 bit ADC"
231987e5972SCameron Grant };
232987e5972SCameron Grant 
23339004e69SCameron Grant static char *ac97extfeature[] = {
23439004e69SCameron Grant 	"variable rate PCM",
23539004e69SCameron Grant 	"double rate PCM",
23639004e69SCameron Grant 	"reserved 1",
23739004e69SCameron Grant 	"variable rate mic",
23839004e69SCameron Grant 	"reserved 2",
23939004e69SCameron Grant 	"reserved 3",
24039004e69SCameron Grant 	"center DAC",
24139004e69SCameron Grant 	"surround DAC",
24239004e69SCameron Grant 	"LFE DAC",
24339004e69SCameron Grant 	"AMAP",
24439004e69SCameron Grant 	"reserved 4",
24539004e69SCameron Grant 	"reserved 5",
24639004e69SCameron Grant 	"reserved 6",
24739004e69SCameron Grant 	"reserved 7",
24839004e69SCameron Grant };
24939004e69SCameron Grant 
250f9eb1409SOrion Hodson u_int16_t
251f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg)
25239004e69SCameron Grant {
2530f55ac6cSCameron Grant 	return AC97_READ(codec->methods, codec->devinfo, reg);
25439004e69SCameron Grant }
25539004e69SCameron Grant 
256f9eb1409SOrion Hodson void
257f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
25839004e69SCameron Grant {
2590f55ac6cSCameron Grant 	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
26039004e69SCameron Grant }
26139004e69SCameron Grant 
262c6d4b83aSOrion Hodson static void
263c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec)
264c6d4b83aSOrion Hodson {
265c6d4b83aSOrion Hodson 	u_int32_t i, ps;
266f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_RESET, 0);
267c6d4b83aSOrion Hodson 	for (i = 0; i < 500; i++) {
268f9eb1409SOrion Hodson 		ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
269c6d4b83aSOrion Hodson 		if (ps == AC97_POWER_STATUS)
270c6d4b83aSOrion Hodson 			return;
271c6d4b83aSOrion Hodson 		DELAY(1000);
272c6d4b83aSOrion Hodson 	}
273a825c6e5SOrion Hodson 	device_printf(codec->dev, "AC97 reset timed out.\n");
274c6d4b83aSOrion Hodson }
275c6d4b83aSOrion Hodson 
27639004e69SCameron Grant int
27739004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate)
27839004e69SCameron Grant {
27939004e69SCameron Grant 	u_int16_t v;
28039004e69SCameron Grant 
28139004e69SCameron Grant 	switch(which) {
28239004e69SCameron Grant 	case AC97_REGEXT_FDACRATE:
28339004e69SCameron Grant 	case AC97_REGEXT_SDACRATE:
28439004e69SCameron Grant 	case AC97_REGEXT_LDACRATE:
28539004e69SCameron Grant 	case AC97_REGEXT_LADCRATE:
28639004e69SCameron Grant 	case AC97_REGEXT_MADCRATE:
28739004e69SCameron Grant 		break;
28839004e69SCameron Grant 
28939004e69SCameron Grant 	default:
29039004e69SCameron Grant 		return -1;
29139004e69SCameron Grant 	}
29239004e69SCameron Grant 
29366ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
29439004e69SCameron Grant 	if (rate != 0) {
29539004e69SCameron Grant 		v = rate;
29639004e69SCameron Grant 		if (codec->extstat & AC97_EXTCAP_DRA)
29739004e69SCameron Grant 			v >>= 1;
298f9eb1409SOrion Hodson 		ac97_wrcd(codec, which, v);
29939004e69SCameron Grant 	}
300f9eb1409SOrion Hodson 	v = ac97_rdcd(codec, which);
30139004e69SCameron Grant 	if (codec->extstat & AC97_EXTCAP_DRA)
30239004e69SCameron Grant 		v <<= 1;
30366ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
30439004e69SCameron Grant 	return v;
30539004e69SCameron Grant }
30639004e69SCameron Grant 
30739004e69SCameron Grant int
30839004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
30939004e69SCameron Grant {
31039004e69SCameron Grant 	mode &= AC97_EXTCAPS;
311647fbfebSOrion Hodson 	if ((mode & ~codec->extcaps) != 0) {
312647fbfebSOrion Hodson 		device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
313647fbfebSOrion Hodson 			      mode);
31439004e69SCameron Grant 		return -1;
315647fbfebSOrion Hodson 	}
31666ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
317f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
318f9eb1409SOrion Hodson 	codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
31966ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
32039004e69SCameron Grant 	return (mode == codec->extstat)? 0 : -1;
32139004e69SCameron Grant }
32239004e69SCameron Grant 
3239ec437a3SCameron Grant u_int16_t
3249ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec)
3259ec437a3SCameron Grant {
3269ec437a3SCameron Grant 	return codec->extstat;
3279ec437a3SCameron Grant }
3289ec437a3SCameron Grant 
3299ec437a3SCameron Grant u_int16_t
3309ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec)
3319ec437a3SCameron Grant {
3329ec437a3SCameron Grant 	return codec->extcaps;
3339ec437a3SCameron Grant }
3349ec437a3SCameron Grant 
3355d91ad67SCameron Grant u_int16_t
3365d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec)
3375d91ad67SCameron Grant {
3385d91ad67SCameron Grant 	return codec->caps;
3395d91ad67SCameron Grant }
3405d91ad67SCameron Grant 
341987e5972SCameron Grant static int
342987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel)
343987e5972SCameron Grant {
344987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
345341f16ccSCameron Grant 
346987e5972SCameron Grant 	if (e->recidx > 0) {
347987e5972SCameron Grant 		int val = e->recidx - 1;
348987e5972SCameron Grant 		val |= val << 8;
34966ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
350f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REG_RECSEL, val);
35166ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
352987e5972SCameron Grant 		return 0;
35339004e69SCameron Grant 	} else
35439004e69SCameron Grant 		return -1;
355987e5972SCameron Grant }
356987e5972SCameron Grant 
357987e5972SCameron Grant static int
358987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
359987e5972SCameron Grant {
360987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
361341f16ccSCameron Grant 
362341f16ccSCameron Grant 	if (e->reg && e->enable && e->bits) {
363e479a8afSCameron Grant 		int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
364987e5972SCameron Grant 
36539004e69SCameron Grant 		if (!e->stereo)
36639004e69SCameron Grant 			right = left;
367987e5972SCameron Grant 		if (e->reg > 0) {
368987e5972SCameron Grant 			left = 100 - left;
369987e5972SCameron Grant 			right = 100 - right;
370987e5972SCameron Grant 		}
371987e5972SCameron Grant 
372987e5972SCameron Grant 		max = (1 << e->bits) - 1;
373987e5972SCameron Grant 		left = (left * max) / 100;
374987e5972SCameron Grant 		right = (right * max) / 100;
375987e5972SCameron Grant 
376987e5972SCameron Grant 		val = (left << 8) | right;
377987e5972SCameron Grant 
378987e5972SCameron Grant 		left = (left * 100) / max;
379987e5972SCameron Grant 		right = (right * 100) / max;
380987e5972SCameron Grant 
381987e5972SCameron Grant 		if (e->reg > 0) {
382987e5972SCameron Grant 			left = 100 - left;
383987e5972SCameron Grant 			right = 100 - right;
384987e5972SCameron Grant 		}
385987e5972SCameron Grant 
386987e5972SCameron Grant 		if (!e->stereo) {
387987e5972SCameron Grant 			val &= max;
388987e5972SCameron Grant 			val <<= e->ofs;
389987e5972SCameron Grant 			if (e->mask) {
390f9eb1409SOrion Hodson 				int cur = ac97_rdcd(codec, e->reg);
391987e5972SCameron Grant 				val |= cur & ~(max << e->ofs);
392987e5972SCameron Grant 			}
393987e5972SCameron Grant 		}
39439004e69SCameron Grant 		if (left == 0 && right == 0 && e->mute == 1)
39539004e69SCameron Grant 			val = AC97_MUTE;
39666ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
397f9eb1409SOrion Hodson 		ac97_wrcd(codec, reg, val);
39866ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
399987e5972SCameron Grant 		return left | (right << 8);
400341f16ccSCameron Grant 	} else {
401341f16ccSCameron Grant 		/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
40239004e69SCameron Grant 		return -1;
403987e5972SCameron Grant 	}
404341f16ccSCameron Grant }
405987e5972SCameron Grant 
406987e5972SCameron Grant #if 0
407987e5972SCameron Grant static int
408987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel)
409987e5972SCameron Grant {
410987e5972SCameron Grant 	struct ac97mixtable_entry *e = &codec->mix[channel];
411987e5972SCameron Grant 	if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
412987e5972SCameron Grant 		int max, val, volume;
413987e5972SCameron Grant 
414987e5972SCameron Grant 		max = (1 << e->bits) - 1;
415f9eb1409SOrion Hodson 		val = ac97_rdcd(code, e->reg);
41639004e69SCameron Grant 		if (val == AC97_MUTE && e->mute == 1)
41739004e69SCameron Grant 			volume = 0;
418987e5972SCameron Grant 		else {
419987e5972SCameron Grant 			if (e->stereo == 0) val >>= e->ofs;
420987e5972SCameron Grant 			val &= max;
421987e5972SCameron Grant 			volume = (val * 100) / max;
422987e5972SCameron Grant 			if (e->reg > 0) volume = 100 - volume;
423987e5972SCameron Grant 		}
424987e5972SCameron Grant 		return volume;
42539004e69SCameron Grant 	} else
42639004e69SCameron Grant 		return -1;
427987e5972SCameron Grant }
428987e5972SCameron Grant #endif
429987e5972SCameron Grant 
430108082c4SOrion Hodson static void
431108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec)
432108082c4SOrion Hodson {
433108082c4SOrion Hodson 	/* Determine what AUXOUT really means, it can be:
434108082c4SOrion Hodson 	 *
435108082c4SOrion Hodson 	 * 1. Headphone out.
436108082c4SOrion Hodson 	 * 2. 4-Channel Out
437108082c4SOrion Hodson 	 * 3. True line level out (effectively master volume).
438108082c4SOrion Hodson 	 *
439108082c4SOrion Hodson 	 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
440108082c4SOrion Hodson 	 */
441108082c4SOrion Hodson 	if (codec->caps & AC97_CAP_HEADPHONE) {
442108082c4SOrion Hodson 		/* XXX We should probably check the AUX_OUT initial value.
443108082c4SOrion Hodson 		 * Leave AC97_MIX_AUXOUT - SOUND_MIXER_MONITOR relationship */
444108082c4SOrion Hodson 		return;
445108082c4SOrion Hodson 	} else if (codec->extcaps & AC97_EXTCAP_SDAC &&
446f9eb1409SOrion Hodson 		   ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
447108082c4SOrion Hodson 		/* 4-Channel Out, add an additional gain setting. */
448108082c4SOrion Hodson 		codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR];
449108082c4SOrion Hodson 	} else {
450108082c4SOrion Hodson 		/* Master volume is/maybe fixed in h/w, not sufficiently
451108082c4SOrion Hodson 		 * clear in spec to blat SOUND_MIXER_MASTER. */
452108082c4SOrion Hodson 		codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR];
453108082c4SOrion Hodson 	}
454108082c4SOrion Hodson 	/* Blat monitor, inappropriate label if we get here */
455108082c4SOrion Hodson 	bzero(&codec->mix[SOUND_MIXER_MONITOR],
456108082c4SOrion Hodson 	      sizeof(codec->mix[SOUND_MIXER_MONITOR]));
457108082c4SOrion Hodson }
458108082c4SOrion Hodson 
45919921b23SOrion Hodson static const char*
46019921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
46119921b23SOrion Hodson {
46219921b23SOrion Hodson 	if (cname == NULL) {
46319921b23SOrion Hodson 		sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
46419921b23SOrion Hodson 		return buf;
46519921b23SOrion Hodson 	}
46619921b23SOrion Hodson 
46719921b23SOrion Hodson 	if (vname == NULL) vname = "Unknown";
46819921b23SOrion Hodson 
46919921b23SOrion Hodson 	if (bootverbose) {
47019921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
47119921b23SOrion Hodson 	} else {
47219921b23SOrion Hodson 		sprintf(buf, "%s %s AC97 Codec", vname, cname);
47319921b23SOrion Hodson 	}
47419921b23SOrion Hodson 	return buf;
47519921b23SOrion Hodson }
47619921b23SOrion Hodson 
477987e5972SCameron Grant static unsigned
47839004e69SCameron Grant ac97_initmixer(struct ac97_info *codec)
479987e5972SCameron Grant {
480f9eb1409SOrion Hodson 	ac97_patch codec_patch;
48119921b23SOrion Hodson 	const char *cname, *vname;
48219921b23SOrion Hodson 	char desc[80];
48319921b23SOrion Hodson 	u_int8_t model, step;
484341f16ccSCameron Grant 	unsigned i, j, k, old;
485987e5972SCameron Grant 	u_int32_t id;
486987e5972SCameron Grant 
48766ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
4880f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
489cd2c103aSCameron Grant 	if (codec->count == 0) {
49004553e63SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
49166ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
49204553e63SCameron Grant 		return ENODEV;
49304553e63SCameron Grant 	}
4949ec437a3SCameron Grant 
495f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
496c6d4b83aSOrion Hodson 	ac97_reset(codec);
497f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
498987e5972SCameron Grant 
499f9eb1409SOrion Hodson 	i = ac97_rdcd(codec, AC97_REG_RESET);
500987e5972SCameron Grant 	codec->caps = i & 0x03ff;
501987e5972SCameron Grant 	codec->se =  (i & 0x7c00) >> 10;
502987e5972SCameron Grant 
503f9eb1409SOrion Hodson 	id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
504e620d959SCameron Grant 	if (id == 0 || id == 0xffffffff) {
505e620d959SCameron Grant 		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
50666ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
507e620d959SCameron Grant 		return ENODEV;
508e620d959SCameron Grant 	}
5096b4b88f7SCameron Grant 
51019921b23SOrion Hodson 	codec->id = id;
511cd2c103aSCameron Grant 	codec->noext = 0;
512f9eb1409SOrion Hodson 	codec_patch = NULL;
51319921b23SOrion Hodson 
51419921b23SOrion Hodson 	cname = NULL;
51519921b23SOrion Hodson 	model = step = 0;
516cd2c103aSCameron Grant 	for (i = 0; ac97codecid[i].id; i++) {
51719921b23SOrion Hodson 		u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
51819921b23SOrion Hodson 		if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
519cd2c103aSCameron Grant 			codec->noext = ac97codecid[i].noext;
520f9eb1409SOrion Hodson 			codec_patch = ac97codecid[i].patch;
52119921b23SOrion Hodson 			cname = ac97codecid[i].name;
52219921b23SOrion Hodson 			model = (id & modelmask) & 0xff;
52319921b23SOrion Hodson 			step = (id & ~modelmask) & 0xff;
52419921b23SOrion Hodson 			break;
52519921b23SOrion Hodson 		}
52619921b23SOrion Hodson 	}
52719921b23SOrion Hodson 
52819921b23SOrion Hodson 	vname = NULL;
52919921b23SOrion Hodson 	for (i = 0; ac97vendorid[i].id; i++) {
53019921b23SOrion Hodson 		if (ac97vendorid[i].id == (id & 0xffffff00)) {
53119921b23SOrion Hodson 			vname = ac97vendorid[i].name;
53219921b23SOrion Hodson 			break;
533cd2c103aSCameron Grant 		}
534cd2c103aSCameron Grant 	}
5356b4b88f7SCameron Grant 
536cd2c103aSCameron Grant 	codec->extcaps = 0;
537cd2c103aSCameron Grant 	codec->extid = 0;
538cd2c103aSCameron Grant 	codec->extstat = 0;
5396a6ee5bbSCameron Grant 	if (!codec->noext) {
540f9eb1409SOrion Hodson 		i = ac97_rdcd(codec, AC97_REGEXT_ID);
5416a6ee5bbSCameron Grant 		if (i != 0xffff) {
54239004e69SCameron Grant 			codec->extcaps = i & 0x3fff;
54339004e69SCameron Grant 			codec->extid =  (i & 0xc000) >> 14;
544f9eb1409SOrion Hodson 			codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
5456b4b88f7SCameron Grant 		}
5466a6ee5bbSCameron Grant 	}
547987e5972SCameron Grant 
548341f16ccSCameron Grant 	for (i = 0; i < 32; i++) {
549108082c4SOrion Hodson 		codec->mix[i] = ac97mixtable_default[i];
550108082c4SOrion Hodson 	}
551108082c4SOrion Hodson 	ac97_fix_auxout(codec);
552f9eb1409SOrion Hodson 	if (codec_patch)
553f9eb1409SOrion Hodson 		codec_patch(codec);
554108082c4SOrion Hodson 
555108082c4SOrion Hodson 	for (i = 0; i < 32; i++) {
55633c878f0SCameron Grant 		k = codec->noext? codec->mix[i].enable : 1;
55733c878f0SCameron Grant 		if (k && (codec->mix[i].reg > 0)) {
558f9eb1409SOrion Hodson 			old = ac97_rdcd(codec, codec->mix[i].reg);
559f9eb1409SOrion Hodson 			ac97_wrcd(codec, codec->mix[i].reg, 0x3f);
560f9eb1409SOrion Hodson 			j = ac97_rdcd(codec, codec->mix[i].reg);
561f9eb1409SOrion Hodson 			ac97_wrcd(codec, codec->mix[i].reg, old);
5626a6ee5bbSCameron Grant 			codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
563341f16ccSCameron Grant 			for (k = 1; j & (1 << k); k++);
564341f16ccSCameron Grant 			codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
565341f16ccSCameron Grant 		}
566341f16ccSCameron Grant 		/* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
567341f16ccSCameron Grant 	}
568987e5972SCameron Grant 
56919921b23SOrion Hodson 	device_printf(codec->dev, "<%s>\n",
57019921b23SOrion Hodson 		      ac97_hw_desc(codec->id, vname, cname, desc));
571a825c6e5SOrion Hodson 
572987e5972SCameron Grant 	if (bootverbose) {
57319921b23SOrion Hodson 		device_printf(codec->dev, "Codec features ");
57439004e69SCameron Grant 		for (i = j = 0; i < 10; i++)
57539004e69SCameron Grant 			if (codec->caps & (1 << i))
57639004e69SCameron Grant 				printf("%s%s", j++? ", " : "", ac97feature[i]);
57739004e69SCameron Grant 		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
578987e5972SCameron Grant 		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
57939004e69SCameron Grant 
58039004e69SCameron Grant 		if (codec->extcaps != 0 || codec->extid) {
58119921b23SOrion Hodson 			device_printf(codec->dev, "%s codec",
58219921b23SOrion Hodson 				      codec->extid? "Secondary" : "Primary");
58339004e69SCameron Grant 			if (codec->extcaps)
58439004e69SCameron Grant 				printf(" extended features ");
58539004e69SCameron Grant 			for (i = j = 0; i < 14; i++)
58639004e69SCameron Grant 				if (codec->extcaps & (1 << i))
58739004e69SCameron Grant 					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
58839004e69SCameron Grant 			printf("\n");
58939004e69SCameron Grant 		}
590987e5972SCameron Grant 	}
591987e5972SCameron Grant 
592f9eb1409SOrion Hodson 	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
59303a00905SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
59466ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
595987e5972SCameron Grant 	return 0;
596987e5972SCameron Grant }
597987e5972SCameron Grant 
5989ec437a3SCameron Grant static unsigned
5999ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec)
6009ec437a3SCameron Grant {
60166ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
6020f55ac6cSCameron Grant 	codec->count = AC97_INIT(codec->methods, codec->devinfo);
6039ec437a3SCameron Grant 	if (codec->count == 0) {
6049ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec init failed\n");
60566ef8af5SCameron Grant 		snd_mtxunlock(codec->lock);
6069ec437a3SCameron Grant 		return ENODEV;
6079ec437a3SCameron Grant 	}
6089ec437a3SCameron Grant 
609f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
610c6d4b83aSOrion Hodson 	ac97_reset(codec);
611f9eb1409SOrion Hodson 	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
6129ec437a3SCameron Grant 
6139ec437a3SCameron Grant 	if (!codec->noext) {
614f9eb1409SOrion Hodson 		ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
615f9eb1409SOrion Hodson 		if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
616b60e55dbSGuido van Rooij 		    != codec->extstat)
6179ec437a3SCameron Grant 			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
618b60e55dbSGuido van Rooij 				      codec->extstat,
619f9eb1409SOrion Hodson 				      ac97_rdcd(codec, AC97_REGEXT_STAT) &
620b60e55dbSGuido van Rooij 				      AC97_EXTCAPS);
6219ec437a3SCameron Grant 	}
6229ec437a3SCameron Grant 
623f9eb1409SOrion Hodson 	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
6249ec437a3SCameron Grant 		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
62566ef8af5SCameron Grant 	snd_mtxunlock(codec->lock);
6269ec437a3SCameron Grant 	return 0;
6279ec437a3SCameron Grant }
6289ec437a3SCameron Grant 
629987e5972SCameron Grant struct ac97_info *
6300f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
631987e5972SCameron Grant {
632987e5972SCameron Grant 	struct ac97_info *codec;
633987e5972SCameron Grant 
6340f55ac6cSCameron Grant 	codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
6350f55ac6cSCameron Grant 	if (codec == NULL)
6360f55ac6cSCameron Grant 		return NULL;
6370f55ac6cSCameron Grant 
63866ef8af5SCameron Grant 	snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
639489c22ebSJohn Baldwin 	codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
640a163d034SWarner Losh 	codec->methods = kobj_create(cls, M_AC97, M_WAITOK);
6410f55ac6cSCameron Grant 	if (codec->methods == NULL) {
64266ef8af5SCameron Grant 		snd_mtxlock(codec->lock);
64366ef8af5SCameron Grant 		snd_mtxfree(codec->lock);
6440f55ac6cSCameron Grant 		free(codec, M_AC97);
6450f55ac6cSCameron Grant 		return NULL;
646987e5972SCameron Grant 	}
6470f55ac6cSCameron Grant 
6480f55ac6cSCameron Grant 	codec->dev = dev;
6490f55ac6cSCameron Grant 	codec->devinfo = devinfo;
65079bb7d52SCameron Grant 	codec->flags = 0;
651987e5972SCameron Grant 	return codec;
652987e5972SCameron Grant }
653987e5972SCameron Grant 
65433dbf14aSCameron Grant void
65533dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec)
65633dbf14aSCameron Grant {
65766ef8af5SCameron Grant 	snd_mtxlock(codec->lock);
6580f55ac6cSCameron Grant 	if (codec->methods != NULL)
6590f55ac6cSCameron Grant 		kobj_delete(codec->methods, M_AC97);
66066ef8af5SCameron Grant 	snd_mtxfree(codec->lock);
6610f55ac6cSCameron Grant 	free(codec, M_AC97);
66233dbf14aSCameron Grant }
66333dbf14aSCameron Grant 
66479bb7d52SCameron Grant void
66579bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val)
66679bb7d52SCameron Grant {
66779bb7d52SCameron Grant 	codec->flags = val;
66879bb7d52SCameron Grant }
66979bb7d52SCameron Grant 
67079bb7d52SCameron Grant u_int32_t
67179bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec)
67279bb7d52SCameron Grant {
67379bb7d52SCameron Grant 	return codec->flags;
67479bb7d52SCameron Grant }
67579bb7d52SCameron Grant 
6760f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
6770f55ac6cSCameron Grant 
678987e5972SCameron Grant static int
67966ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m)
680987e5972SCameron Grant {
681987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
682341f16ccSCameron Grant 	u_int32_t i, mask;
683341f16ccSCameron Grant 
68439004e69SCameron Grant 	if (codec == NULL)
68539004e69SCameron Grant 		return -1;
686341f16ccSCameron Grant 
687e620d959SCameron Grant 	if (ac97_initmixer(codec))
688e620d959SCameron Grant 		return -1;
689341f16ccSCameron Grant 
690341f16ccSCameron Grant 	mask = 0;
691341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
692341f16ccSCameron Grant 		mask |= codec->mix[i].enable? 1 << i : 0;
693341f16ccSCameron Grant 	mix_setdevs(m, mask);
694341f16ccSCameron Grant 
695341f16ccSCameron Grant 	mask = 0;
696341f16ccSCameron Grant 	for (i = 0; i < 32; i++)
697341f16ccSCameron Grant 		mask |= codec->mix[i].recidx? 1 << i : 0;
698341f16ccSCameron Grant 	mix_setrecdevs(m, mask);
699987e5972SCameron Grant 	return 0;
700987e5972SCameron Grant }
701987e5972SCameron Grant 
702987e5972SCameron Grant static int
70366ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m)
70433dbf14aSCameron Grant {
70533dbf14aSCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
706341f16ccSCameron Grant 
70733dbf14aSCameron Grant 	if (codec == NULL)
70833dbf14aSCameron Grant 		return -1;
70933dbf14aSCameron Grant 	/*
71033dbf14aSCameron Grant 	if (ac97_uninitmixer(codec))
71133dbf14aSCameron Grant 		return -1;
71233dbf14aSCameron Grant 	*/
71333dbf14aSCameron Grant 	ac97_destroy(codec);
71433dbf14aSCameron Grant 	return 0;
71533dbf14aSCameron Grant }
71633dbf14aSCameron Grant 
71733dbf14aSCameron Grant static int
71866ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m)
7199ec437a3SCameron Grant {
7209ec437a3SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
7219ec437a3SCameron Grant 
7229ec437a3SCameron Grant 	if (codec == NULL)
7239ec437a3SCameron Grant 		return -1;
7249ec437a3SCameron Grant 	return ac97_reinitmixer(codec);
7259ec437a3SCameron Grant }
7269ec437a3SCameron Grant 
7279ec437a3SCameron Grant static int
72866ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
729987e5972SCameron Grant {
730987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
731341f16ccSCameron Grant 
73239004e69SCameron Grant 	if (codec == NULL)
73339004e69SCameron Grant 		return -1;
734987e5972SCameron Grant 	return ac97_setmixer(codec, dev, left, right);
735987e5972SCameron Grant }
736987e5972SCameron Grant 
737987e5972SCameron Grant static int
73866ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
739987e5972SCameron Grant {
740987e5972SCameron Grant 	int i;
741987e5972SCameron Grant 	struct ac97_info *codec = mix_getdevinfo(m);
742341f16ccSCameron Grant 
74339004e69SCameron Grant 	if (codec == NULL)
74439004e69SCameron Grant 		return -1;
745987e5972SCameron Grant 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
74639004e69SCameron Grant 		if ((src & (1 << i)) != 0)
74739004e69SCameron Grant 			break;
748987e5972SCameron Grant 	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
749987e5972SCameron Grant }
750987e5972SCameron Grant 
7510f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = {
7520f55ac6cSCameron Grant     	KOBJMETHOD(mixer_init,		ac97mix_init),
7530f55ac6cSCameron Grant     	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
7540f55ac6cSCameron Grant     	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
7550f55ac6cSCameron Grant     	KOBJMETHOD(mixer_set,		ac97mix_set),
7560f55ac6cSCameron Grant     	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
7570f55ac6cSCameron Grant 	{ 0, 0 }
758987e5972SCameron Grant };
7590f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer);
7600f55ac6cSCameron Grant 
7610f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
7620f55ac6cSCameron Grant 
7630f55ac6cSCameron Grant kobj_class_t
7640f55ac6cSCameron Grant ac97_getmixerclass(void)
7650f55ac6cSCameron Grant {
7660f55ac6cSCameron Grant 	return &ac97mixer_class;
7670f55ac6cSCameron Grant }
7680f55ac6cSCameron Grant 
769987e5972SCameron Grant 
770