ac97.c revision 162974
1139749Simp/*-
2119853Scg * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
350724Scg * All rights reserved.
450724Scg *
550724Scg * Redistribution and use in source and binary forms, with or without
650724Scg * modification, are permitted provided that the following conditions
750724Scg * are met:
850724Scg * 1. Redistributions of source code must retain the above copyright
950724Scg *    notice, this list of conditions and the following disclaimer.
1050724Scg * 2. Redistributions in binary form must reproduce the above copyright
1150724Scg *    notice, this list of conditions and the following disclaimer in the
1250724Scg *    documentation and/or other materials provided with the distribution.
1350724Scg *
1450724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1550724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1650724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1750724Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1850724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1950724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2050724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2150724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2250724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2350724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2450724Scg * SUCH DAMAGE.
2550724Scg */
2650724Scg
2753465Scg#include <dev/sound/pcm/sound.h>
2853465Scg#include <dev/sound/pcm/ac97.h>
29109818Sorion#include <dev/sound/pcm/ac97_patch.h>
3050724Scg
3170134Scg#include "mixer_if.h"
3250724Scg
3382180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/ac97.c 162974 2006-10-02 20:46:34Z ariff $");
3482180Scg
3570134ScgMALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
3650724Scg
3778668Scgstruct ac97mixtable_entry {
38113783Sorion	int	 reg:8;		/* register index		*/
39113783Sorion				/* reg < 0 if inverted polarity	*/
40113783Sorion	unsigned bits:4;	/* width of control field	*/
41113783Sorion	unsigned ofs:4;		/* offset (only if stereo=0)	*/
42113783Sorion	unsigned stereo:1;	/* set for stereo controls	*/
43113783Sorion	unsigned mute:1;	/* bit15 is MUTE		*/
44113783Sorion	unsigned recidx:4;	/* index in rec mux		*/
45113783Sorion	unsigned mask:1;	/* use only masked bits		*/
46113783Sorion	unsigned enable:1;	/* entry is enabled		*/
4778668Scg};
4878668Scg
4974763Scg#define AC97_NAMELEN	16
5074763Scgstruct ac97_info {
5174763Scg	kobj_t methods;
5274763Scg	device_t dev;
5374763Scg	void *devinfo;
54111679Sorion	u_int32_t id;
5574763Scg	unsigned count, caps, se, extcaps, extid, extstat, noext:1;
5678668Scg	u_int32_t flags;
5774763Scg	struct ac97mixtable_entry mix[32];
5874763Scg	char name[AC97_NAMELEN];
59107285Scg	struct mtx *lock;
6074763Scg};
6174763Scg
62111679Sorionstruct ac97_vendorid {
63111679Sorion	u_int32_t   id;
64111679Sorion	const char *name;
65111679Sorion};
66111679Sorion
6750724Scgstruct ac97_codecid {
68111679Sorion	u_int32_t  id;
69111679Sorion	u_int8_t   stepmask;
70111679Sorion	u_int8_t   noext:1;
71111679Sorion	char 	  *name;
72109818Sorion	ac97_patch patch;
7350724Scg};
7450724Scg
7550724Scgstatic const struct ac97mixtable_entry ac97mixtable_default[32] = {
76113783Sorion    /*	[offset]			reg	     bits of st mu re mk en */
7766013Scg	[SOUND_MIXER_VOLUME]	= { AC97_MIX_MASTER, 	5, 0, 1, 1, 6, 0, 1 },
78119209Sorion	[SOUND_MIXER_OGAIN]	= { AC97_MIX_AUXOUT, 	5, 0, 1, 1, 0, 0, 0 },
7966013Scg	[SOUND_MIXER_PHONEOUT]	= { AC97_MIX_MONO, 	5, 0, 0, 1, 7, 0, 0 },
8066013Scg	[SOUND_MIXER_BASS]	= { AC97_MIX_TONE, 	4, 8, 0, 0, 0, 1, 0 },
8166013Scg	[SOUND_MIXER_TREBLE]	= { AC97_MIX_TONE, 	4, 0, 0, 0, 0, 1, 0 },
8266013Scg	[SOUND_MIXER_PCM]	= { AC97_MIX_PCM, 	5, 0, 1, 1, 0, 0, 1 },
8366013Scg	[SOUND_MIXER_SPEAKER]	= { AC97_MIX_BEEP, 	4, 1, 0, 1, 0, 0, 0 },
8466013Scg	[SOUND_MIXER_LINE]	= { AC97_MIX_LINE, 	5, 0, 1, 1, 5, 0, 1 },
8566013Scg	[SOUND_MIXER_PHONEIN]	= { AC97_MIX_PHONE, 	5, 0, 0, 1, 8, 0, 0 },
86113783Sorion	[SOUND_MIXER_MIC] 	= { AC97_MIX_MIC, 	5, 0, 0, 1, 1, 1, 1 },
87113783Sorion	/* use igain for the mic 20dB boost */
88113783Sorion	[SOUND_MIXER_IGAIN] 	= { -AC97_MIX_MIC, 	1, 6, 0, 0, 0, 1, 1 },
8966013Scg	[SOUND_MIXER_CD]	= { AC97_MIX_CD, 	5, 0, 1, 1, 2, 0, 1 },
9066013Scg	[SOUND_MIXER_LINE1]	= { AC97_MIX_AUX, 	5, 0, 1, 1, 4, 0, 0 },
9166013Scg	[SOUND_MIXER_VIDEO]	= { AC97_MIX_VIDEO, 	5, 0, 1, 1, 3, 0, 0 },
9266013Scg	[SOUND_MIXER_RECLEV]	= { -AC97_MIX_RGAIN, 	4, 0, 1, 1, 0, 0, 1 }
9350724Scg};
9450724Scg
95111679Sorionstatic const struct ac97_vendorid ac97vendorid[] = {
96111679Sorion	{ 0x41445300, "Analog Devices" },
97111679Sorion	{ 0x414b4d00, "Asahi Kasei" },
98111679Sorion	{ 0x414c4300, "Realtek" },
99111679Sorion	{ 0x414c4700, "Avance Logic" },
100111679Sorion	{ 0x43525900, "Cirrus Logic" },
101111679Sorion	{ 0x434d4900, "C-Media Electronics" },
102111679Sorion	{ 0x43585400, "Conexant" },
103124875Smatk	{ 0x44543000, "Diamond Technology" },
104113788Sorion	{ 0x454d4300, "eMicro" },
105111679Sorion	{ 0x45838300, "ESS Technology" },
106124875Smatk	{ 0x48525300, "Intersil" },
107111679Sorion	{ 0x49434500, "ICEnsemble" },
108124875Smatk	{ 0x49544500, "ITE, Inc." },
109111679Sorion	{ 0x4e534300, "National Semiconductor" },
110111679Sorion	{ 0x50534300, "Philips Semiconductor" },
111111679Sorion	{ 0x83847600, "SigmaTel" },
112124875Smatk	{ 0x53494c00, "Silicon Laboratories" },
113111679Sorion	{ 0x54524100, "TriTech" },
114124875Smatk	{ 0x54584e00, "Texas Instruments" },
115111679Sorion	{ 0x56494100, "VIA Technologies" },
116124875Smatk	{ 0x57454300, "Winbond" },
117111679Sorion	{ 0x574d4c00, "Wolfson" },
118111679Sorion	{ 0x594d4800, "Yamaha" },
119149949Snetchild	/*
120149949Snetchild	 * XXX This is a fluke, really! The real vendor
121149949Snetchild	 * should be SigmaTel, not this! This should be
122149949Snetchild	 * removed someday!
123149949Snetchild	 */
124119547Sorion	{ 0x01408300, "Creative" },
125111679Sorion	{ 0x00000000, NULL }
126111679Sorion};
127111679Sorion
12850724Scgstatic struct ac97_codecid ac97codecid[] = {
129111679Sorion	{ 0x41445303, 0x00, 0, "AD1819",	0 },
130111679Sorion	{ 0x41445340, 0x00, 0, "AD1881",	0 },
131111679Sorion	{ 0x41445348, 0x00, 0, "AD1881A",	0 },
132111679Sorion	{ 0x41445360, 0x00, 0, "AD1885",	0 },
133111679Sorion	{ 0x41445361, 0x00, 0, "AD1886", 	ad1886_patch },
134113057Sorion	{ 0x41445362, 0x00, 0, "AD1887", 	0 },
135113057Sorion	{ 0x41445363, 0x00, 0, "AD1886A", 	0 },
136144865Sscottl	{ 0x41445368, 0x00, 0, "AD1888", 	ad198x_patch },
137119209Sorion	{ 0x41445370, 0x00, 0, "AD1980",	ad198x_patch },
138113057Sorion	{ 0x41445372, 0x00, 0, "AD1981A",	0 },
139113057Sorion	{ 0x41445374, 0x00, 0, "AD1981B",	ad1981b_patch },
140119209Sorion	{ 0x41445375, 0x00, 0, "AD1985",	ad198x_patch },
141154094Sariff	{ 0x41445378, 0x00, 0, "AD1986",	ad198x_patch },
142111679Sorion	{ 0x414b4d00, 0x00, 1, "AK4540", 	0 },
143111679Sorion	{ 0x414b4d01, 0x00, 1, "AK4542", 	0 },
144111679Sorion	{ 0x414b4d02, 0x00, 1, "AK4543", 	0 },
145124875Smatk	{ 0x414b4d06, 0x00, 0, "AK4544A",	0 },
146124875Smatk	{ 0x454b4d07, 0x00, 0, "AK4545",	0 },
147111679Sorion	{ 0x414c4320, 0x0f, 0, "ALC100",	0 },
148119209Sorion	{ 0x414c4730, 0x0f, 0, "ALC101",	0 },
149111679Sorion	{ 0x414c4710, 0x0f, 0, "ALC200", 	0 },
150111679Sorion	{ 0x414c4740, 0x0f, 0, "ALC202", 	0 },
151111679Sorion	{ 0x414c4720, 0x0f, 0, "ALC650", 	0 },
152152815Sariff	{ 0x414c4752, 0x0f, 0, "ALC250",	0 },
153121032Sdes	{ 0x414c4760, 0x0f, 0, "ALC655",	0 },
154152939Syongari	{ 0x414c4770, 0x0f, 0, "ALC203",	0 },
155122515Skuriyama	{ 0x414c4780, 0x0f, 0, "ALC658",	0 },
156124875Smatk	{ 0x414c4790, 0x0f, 0, "ALC850",	0 },
157111679Sorion	{ 0x43525900, 0x07, 0, "CS4297", 	0 },
158111679Sorion	{ 0x43525910, 0x07, 0, "CS4297A", 	0 },
159111679Sorion	{ 0x43525920, 0x07, 0, "CS4294/98",	0 },
160129044Ssanpei	{ 0x4352592d, 0x07, 0, "CS4294",	0 },
161111679Sorion	{ 0x43525930, 0x07, 0, "CS4299",	0 },
162111679Sorion	{ 0x43525940, 0x07, 0, "CS4201",	0 },
163112150Sorion	{ 0x43525958, 0x07, 0, "CS4205",	0 },
164111679Sorion	{ 0x43525960, 0x07, 0, "CS4291A",	0 },
165150825Snetchild	{ 0x434d4961, 0x00, 0, "CMI9739",	cmi9739_patch },
166111679Sorion	{ 0x434d4941, 0x00, 0, "CMI9738",	0 },
167150825Snetchild	{ 0x434d4978, 0x00, 0, "CMI9761",	0 },
168150825Snetchild	{ 0x434d4982, 0x00, 0, "CMI9761",	0 },
169150825Snetchild	{ 0x434d4983, 0x00, 0, "CMI9761",	0 },
170124875Smatk	{ 0x43585421, 0x00, 0, "HSD11246",	0 },
171124875Smatk	{ 0x43585428, 0x07, 0, "CX20468",	0 },
172152422Sariff	{ 0x43585430, 0x00, 0, "CX20468-21",	0 },
173124875Smatk	{ 0x44543000, 0x00, 0, "DT0398",	0 },
174113788Sorion	{ 0x454d4323, 0x00, 0, "EM28023",	0 },
175113788Sorion	{ 0x454d4328, 0x00, 0, "EM28028",	0 },
176111679Sorion	{ 0x45838308, 0x00, 0, "ES1988",	0 }, /* Formerly ES1921(?) */
177124875Smatk	{ 0x48525300, 0x00, 0, "HMP9701",	0 },
178111679Sorion	{ 0x49434501, 0x00, 0, "ICE1230",	0 },
179111679Sorion	{ 0x49434511, 0x00, 0, "ICE1232",	0 },
180111679Sorion	{ 0x49434514, 0x00, 0, "ICE1232A",	0 },
181119250Sorion	{ 0x49434551, 0x03, 0, "VT1616",	0 }, /* Via badged ICE */
182124875Smatk	{ 0x49544520, 0x00, 0, "ITE2226E",	0 },
183124875Smatk	{ 0x49544560, 0x07, 0, "ITE2646E",	0 }, /* XXX: patch needed */
184111679Sorion	{ 0x4e534340, 0x00, 0, "LM4540",	0 }, /* Spec blank on revid */
185111679Sorion	{ 0x4e534343, 0x00, 0, "LM4543",	0 }, /* Ditto */
186111679Sorion	{ 0x4e534346, 0x00, 0, "LM4546A",	0 },
187111679Sorion	{ 0x4e534348, 0x00, 0, "LM4548A",	0 },
188124875Smatk	{ 0x4e534331, 0x00, 0, "LM4549",	0 },
189111679Sorion	{ 0x4e534349, 0x00, 0, "LM4549A",	0 },
190111679Sorion	{ 0x4e534350, 0x00, 0, "LM4550",	0 },
191111679Sorion	{ 0x50534301, 0x00, 0, "UCB1510",	0 },
192111679Sorion	{ 0x50534304, 0x00, 0, "UCB1400",	0 },
193111679Sorion	{ 0x83847600, 0x00, 0, "STAC9700/83/84",	0 },
194111679Sorion	{ 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
195113788Sorion	{ 0x83847605, 0x00, 0, "STAC9704",	0 },
196111679Sorion	{ 0x83847608, 0x00, 0, "STAC9708/11",	0 },
197111679Sorion	{ 0x83847609, 0x00, 0, "STAC9721/23",	0 },
198111679Sorion	{ 0x83847644, 0x00, 0, "STAC9744/45",	0 },
199111679Sorion	{ 0x83847650, 0x00, 0, "STAC9750/51",	0 },
200111679Sorion	{ 0x83847652, 0x00, 0, "STAC9752/53",	0 },
201111679Sorion	{ 0x83847656, 0x00, 0, "STAC9756/57",	0 },
202111679Sorion	{ 0x83847658, 0x00, 0, "STAC9758/59",	0 },
203111679Sorion	{ 0x83847660, 0x00, 0, "STAC9760/61",	0 }, /* Extrapolated */
204111679Sorion	{ 0x83847662, 0x00, 0, "STAC9762/63",	0 }, /* Extrapolated */
205150825Snetchild	{ 0x83847666, 0x00, 0, "STAC9766/67",	0 },
206111679Sorion	{ 0x53494c22, 0x00, 0, "Si3036",	0 },
207111679Sorion	{ 0x53494c23, 0x00, 0, "Si3038",	0 },
208111679Sorion	{ 0x54524103, 0x00, 0, "TR28023",	0 }, /* Extrapolated */
209111679Sorion	{ 0x54524106, 0x00, 0, "TR28026",	0 },
210111679Sorion	{ 0x54524108, 0x00, 0, "TR28028",	0 },
211111679Sorion	{ 0x54524123, 0x00, 0, "TR28602",	0 },
212124875Smatk	{ 0x54524e03, 0x07, 0, "TLV320AIC27",	0 },
213124875Smatk	{ 0x54584e20, 0x00, 0, "TLC320AD90",	0 },
214111679Sorion	{ 0x56494161, 0x00, 0, "VIA1612A",      0 },
215152422Sariff	{ 0x56494170, 0x00, 0, "VIA1617A",      0 },
216111679Sorion	{ 0x574d4c00, 0x00, 0, "WM9701A",	0 },
217111679Sorion	{ 0x574d4c03, 0x00, 0, "WM9703/4/7/8",	0 },
218111679Sorion	{ 0x574d4c04, 0x00, 0, "WM9704Q",	0 },
219111679Sorion	{ 0x574d4c05, 0x00, 0, "WM9705/10",	0 },
220124875Smatk	{ 0x574d4d09, 0x00, 0, "WM9709",	0 },
221124875Smatk	{ 0x574d4c12, 0x00, 0, "WM9711/12",	0 }, /* XXX: patch needed */
222124875Smatk	{ 0x57454301, 0x00, 0, "W83971D",	0 },
223111679Sorion	{ 0x594d4800, 0x00, 0, "YMF743",	0 },
224111679Sorion	{ 0x594d4802, 0x00, 0, "YMF752",	0 },
225111679Sorion	{ 0x594d4803, 0x00, 0, "YMF753",	0 },
226149949Snetchild	/*
227149949Snetchild	 * XXX This is a fluke, really! The real codec
228149949Snetchild	 * should be STAC9704, not this! This should be
229149949Snetchild	 * removed someday!
230149949Snetchild	 */
231119547Sorion	{ 0x01408384, 0x00, 0, "EV1938",	0 },
232111679Sorion	{ 0, 0, 0, NULL, 0 }
23350724Scg};
23450724Scg
23550724Scgstatic char *ac97enhancement[] = {
23660960Scg	"no 3D Stereo Enhancement",
23750724Scg	"Analog Devices Phat Stereo",
23850724Scg	"Creative Stereo Enhancement",
23950724Scg	"National Semi 3D Stereo Enhancement",
24050724Scg	"Yamaha Ymersion",
24150724Scg	"BBE 3D Stereo Enhancement",
24250724Scg	"Crystal Semi 3D Stereo Enhancement",
24350724Scg	"Qsound QXpander",
24450724Scg	"Spatializer 3D Stereo Enhancement",
24550724Scg	"SRS 3D Stereo Enhancement",
24650724Scg	"Platform Tech 3D Stereo Enhancement",
24750724Scg	"AKM 3D Audio",
24850724Scg	"Aureal Stereo Enhancement",
24950724Scg	"Aztech 3D Enhancement",
25050724Scg	"Binaura 3D Audio Enhancement",
25150724Scg	"ESS Technology Stereo Enhancement",
25250724Scg	"Harman International VMAx",
25350724Scg	"Nvidea 3D Stereo Enhancement",
25450724Scg	"Philips Incredible Sound",
25550724Scg	"Texas Instruments 3D Stereo Enhancement",
25650724Scg	"VLSI Technology 3D Stereo Enhancement",
25750724Scg	"TriTech 3D Stereo Enhancement",
25850724Scg	"Realtek 3D Stereo Enhancement",
25950724Scg	"Samsung 3D Stereo Enhancement",
26050724Scg	"Wolfson Microelectronics 3D Enhancement",
26150724Scg	"Delta Integration 3D Enhancement",
26250724Scg	"SigmaTel 3D Enhancement",
26350724Scg	"Reserved 27",
26450724Scg	"Rockwell 3D Stereo Enhancement",
26550724Scg	"Reserved 29",
26650724Scg	"Reserved 30",
26750724Scg	"Reserved 31"
26850724Scg};
26950724Scg
27050724Scgstatic char *ac97feature[] = {
27150724Scg	"mic channel",
27250724Scg	"reserved",
27350724Scg	"tone",
27450724Scg	"simulated stereo",
27550724Scg	"headphone",
27650724Scg	"bass boost",
27750724Scg	"18 bit DAC",
27850724Scg	"20 bit DAC",
27950724Scg	"18 bit ADC",
28050724Scg	"20 bit ADC"
28150724Scg};
28250724Scg
28358384Scgstatic char *ac97extfeature[] = {
28458384Scg	"variable rate PCM",
28558384Scg	"double rate PCM",
28658384Scg	"reserved 1",
28758384Scg	"variable rate mic",
28858384Scg	"reserved 2",
28958384Scg	"reserved 3",
29058384Scg	"center DAC",
29158384Scg	"surround DAC",
29258384Scg	"LFE DAC",
29358384Scg	"AMAP",
29458384Scg	"reserved 4",
29558384Scg	"reserved 5",
29658384Scg	"reserved 6",
29758384Scg	"reserved 7",
29858384Scg};
29958384Scg
300109818Sorionu_int16_t
301109818Sorionac97_rdcd(struct ac97_info *codec, int reg)
30258384Scg{
303149949Snetchild	if (codec->flags & AC97_F_RDCD_BUG) {
304149949Snetchild		u_int16_t i[2], j = 100;
305149949Snetchild
306149949Snetchild		i[0] = AC97_READ(codec->methods, codec->devinfo, reg);
307149949Snetchild		i[1] = AC97_READ(codec->methods, codec->devinfo, reg);
308149949Snetchild		while (i[0] != i[1] && j)
309149949Snetchild			i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg);
310149949Snetchild#if 0
311149949Snetchild		if (j < 100) {
312149949Snetchild			device_printf(codec->dev, "%s(): Inconsistent register value at"
313149949Snetchild					" 0x%08x (retry: %d)\n", __func__, reg, 100 - j);
314149949Snetchild		}
315149949Snetchild#endif
316149949Snetchild		return i[!(j & 1)];
317149949Snetchild	}
31870134Scg	return AC97_READ(codec->methods, codec->devinfo, reg);
31958384Scg}
32058384Scg
321109818Sorionvoid
322109818Sorionac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
32358384Scg{
32470134Scg	AC97_WRITE(codec->methods, codec->devinfo, reg, val);
32558384Scg}
32658384Scg
32795499Sorionstatic void
32895499Sorionac97_reset(struct ac97_info *codec)
32995499Sorion{
33095499Sorion	u_int32_t i, ps;
331109818Sorion	ac97_wrcd(codec, AC97_REG_RESET, 0);
33295499Sorion	for (i = 0; i < 500; i++) {
333109818Sorion		ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
334107285Scg		if (ps == AC97_POWER_STATUS)
33595499Sorion			return;
33695499Sorion		DELAY(1000);
33795499Sorion	}
338110258Sorion	device_printf(codec->dev, "AC97 reset timed out.\n");
33995499Sorion}
34095499Sorion
34158384Scgint
34258384Scgac97_setrate(struct ac97_info *codec, int which, int rate)
34358384Scg{
34458384Scg	u_int16_t v;
34558384Scg
34658384Scg	switch(which) {
347107285Scg	case AC97_REGEXT_FDACRATE:
34858384Scg	case AC97_REGEXT_SDACRATE:
34958384Scg	case AC97_REGEXT_LDACRATE:
350107285Scg	case AC97_REGEXT_LADCRATE:
35158384Scg	case AC97_REGEXT_MADCRATE:
35258384Scg		break;
35358384Scg
35458384Scg	default:
35558384Scg		return -1;
35658384Scg	}
35758384Scg
35874763Scg	snd_mtxlock(codec->lock);
35958384Scg	if (rate != 0) {
36058384Scg		v = rate;
36158384Scg		if (codec->extstat & AC97_EXTCAP_DRA)
36258384Scg			v >>= 1;
363109818Sorion		ac97_wrcd(codec, which, v);
36458384Scg	}
365109818Sorion	v = ac97_rdcd(codec, which);
36658384Scg	if (codec->extstat & AC97_EXTCAP_DRA)
36758384Scg		v <<= 1;
36874763Scg	snd_mtxunlock(codec->lock);
36958384Scg	return v;
37058384Scg}
37158384Scg
37258384Scgint
37358384Scgac97_setextmode(struct ac97_info *codec, u_int16_t mode)
37458384Scg{
37558384Scg	mode &= AC97_EXTCAPS;
37686707Sorion	if ((mode & ~codec->extcaps) != 0) {
377107285Scg		device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
37886707Sorion			      mode);
37958384Scg		return -1;
38086707Sorion	}
38174763Scg	snd_mtxlock(codec->lock);
382109818Sorion	ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
383109818Sorion	codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
38474763Scg	snd_mtxunlock(codec->lock);
38558384Scg	return (mode == codec->extstat)? 0 : -1;
38658384Scg}
38758384Scg
38867652Scgu_int16_t
38967652Scgac97_getextmode(struct ac97_info *codec)
39067652Scg{
39167652Scg	return codec->extstat;
39267652Scg}
39367652Scg
39467652Scgu_int16_t
39567652Scgac97_getextcaps(struct ac97_info *codec)
39667652Scg{
39767652Scg	return codec->extcaps;
39867652Scg}
39967652Scg
40083612Scgu_int16_t
40183612Scgac97_getcaps(struct ac97_info *codec)
40283612Scg{
40383612Scg	return codec->caps;
40483612Scg}
40583612Scg
40650724Scgstatic int
40750724Scgac97_setrecsrc(struct ac97_info *codec, int channel)
40850724Scg{
40950724Scg	struct ac97mixtable_entry *e = &codec->mix[channel];
41066013Scg
41150724Scg	if (e->recidx > 0) {
41250724Scg		int val = e->recidx - 1;
41350724Scg		val |= val << 8;
41474763Scg		snd_mtxlock(codec->lock);
415109818Sorion		ac97_wrcd(codec, AC97_REG_RECSEL, val);
41674763Scg		snd_mtxunlock(codec->lock);
41750724Scg		return 0;
41858384Scg	} else
41958384Scg		return -1;
42050724Scg}
42150724Scg
42250724Scgstatic int
42350724Scgac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
42450724Scg{
42550724Scg	struct ac97mixtable_entry *e = &codec->mix[channel];
42666013Scg
42766013Scg	if (e->reg && e->enable && e->bits) {
428113783Sorion		int mask, max, val, reg;
42950724Scg
430113783Sorion		reg = (e->reg >= 0) ? e->reg : -e->reg;	/* AC97 register    */
431113783Sorion		max = (1 << e->bits) - 1;		/* actual range	    */
432113783Sorion		mask = (max << 8) | max;		/* bits of interest */
433113783Sorion
43458384Scg		if (!e->stereo)
43558384Scg			right = left;
436113783Sorion
437113783Sorion		/*
438113783Sorion		 * Invert the range if the polarity requires so,
439113783Sorion		 * then scale to 0..max-1 to compute the value to
440113783Sorion		 * write into the codec, and scale back to 0..100
441113783Sorion		 * for the return value.
442113783Sorion		 */
44350724Scg		if (e->reg > 0) {
44450724Scg			left = 100 - left;
44550724Scg			right = 100 - right;
44650724Scg		}
44750724Scg
44850724Scg		left = (left * max) / 100;
44950724Scg		right = (right * max) / 100;
45050724Scg
45150724Scg		val = (left << 8) | right;
45250724Scg
45350724Scg		left = (left * 100) / max;
45450724Scg		right = (right * 100) / max;
45550724Scg
45650724Scg		if (e->reg > 0) {
45750724Scg			left = 100 - left;
45850724Scg			right = 100 - right;
45950724Scg		}
46050724Scg
461113783Sorion		/*
462113783Sorion		 * For mono controls, trim val and mask, also taking
463119209Sorion		 * care of e->ofs (offset of control field).
464113783Sorion		 */
465113783Sorion		if (e->ofs) {
46650724Scg			val &= max;
46750724Scg			val <<= e->ofs;
468113783Sorion			mask = (max << e->ofs);
46950724Scg		}
470113783Sorion
471113783Sorion		/*
472113783Sorion		 * If we have a mute bit, add it to the mask and
473113783Sorion		 * update val and set mute if both channels require a
474113783Sorion		 * zero volume.
475113783Sorion		 */
476113783Sorion		if (e->mute == 1) {
477113783Sorion			mask |= AC97_MUTE;
478113783Sorion			if (left == 0 && right == 0)
479113783Sorion				val = AC97_MUTE;
480113783Sorion		}
481113783Sorion
482113783Sorion		/*
483113783Sorion		 * If the mask bit is set, do not alter the other bits.
484113783Sorion		 */
48574763Scg		snd_mtxlock(codec->lock);
486113783Sorion		if (e->mask) {
487149949Snetchild			int cur = ac97_rdcd(codec, reg);
488113783Sorion			val |= cur & ~(mask);
489113783Sorion		}
490109818Sorion		ac97_wrcd(codec, reg, val);
49174763Scg		snd_mtxunlock(codec->lock);
49250724Scg		return left | (right << 8);
49366013Scg	} else {
494149949Snetchild#if 0
495149949Snetchild		printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable);
496149949Snetchild#endif
49758384Scg		return -1;
49866013Scg	}
49950724Scg}
50050724Scg
501102302Sorionstatic void
502102302Sorionac97_fix_auxout(struct ac97_info *codec)
503102302Sorion{
504119375Sorion	int keep_ogain;
505119375Sorion
506119209Sorion	/*
507119375Sorion	 * By default, The ac97 aux_out register (0x04) corresponds to OSS's
508119375Sorion	 * OGAIN setting.
509102302Sorion	 *
510119375Sorion	 * We first check whether aux_out is a valid register.  If not
511119375Sorion	 * we may not want to keep ogain.
512119209Sorion	 */
513119375Sorion	keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000;
514119209Sorion
515119209Sorion	/*
516119209Sorion	 * Determine what AUX_OUT really means, it can be:
517119209Sorion	 *
518102302Sorion	 * 1. Headphone out.
519102302Sorion	 * 2. 4-Channel Out
520102302Sorion	 * 3. True line level out (effectively master volume).
521102302Sorion	 *
522102302Sorion	 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
523102302Sorion	 */
524119209Sorion	if (codec->extcaps & AC97_EXTCAP_SDAC &&
525119209Sorion	    ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
526119375Sorion		codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND;
527119375Sorion		keep_ogain = 1;
528102302Sorion	}
529119375Sorion
530119375Sorion	if (keep_ogain == 0) {
531119375Sorion		bzero(&codec->mix[SOUND_MIXER_OGAIN],
532119375Sorion		      sizeof(codec->mix[SOUND_MIXER_OGAIN]));
533119375Sorion	}
534102302Sorion}
535102302Sorion
536119209Sorionstatic void
537119209Sorionac97_fix_tone(struct ac97_info *codec)
538119209Sorion{
539119209Sorion	/* Hide treble and bass if they don't exist */
540119209Sorion	if ((codec->caps & AC97_CAP_TONE) == 0) {
541119209Sorion		bzero(&codec->mix[SOUND_MIXER_BASS],
542119209Sorion		      sizeof(codec->mix[SOUND_MIXER_BASS]));
543119209Sorion		bzero(&codec->mix[SOUND_MIXER_TREBLE],
544119209Sorion		      sizeof(codec->mix[SOUND_MIXER_TREBLE]));
545119209Sorion	}
546119209Sorion}
547119209Sorion
548150825Snetchildstatic const char*
549150825Snetchildac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
550150825Snetchild{
551150825Snetchild	if (cname == NULL) {
552150825Snetchild		sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
553150825Snetchild		return buf;
554150825Snetchild	}
555150825Snetchild
556150825Snetchild	if (vname == NULL) vname = "Unknown";
557150825Snetchild
558150825Snetchild	if (bootverbose) {
559150825Snetchild		sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
560150825Snetchild	} else {
561150825Snetchild		sprintf(buf, "%s %s AC97 Codec", vname, cname);
562150825Snetchild	}
563150825Snetchild	return buf;
564150825Snetchild}
565150825Snetchild
566150825Snetchildstatic unsigned
567150825Snetchildac97_initmixer(struct ac97_info *codec)
568150825Snetchild{
569150825Snetchild	ac97_patch codec_patch;
570150825Snetchild	const char *cname, *vname;
571150825Snetchild	char desc[80];
572150825Snetchild	u_int8_t model, step;
573150825Snetchild	unsigned i, j, k, bit, old;
574150825Snetchild	u_int32_t id;
575150825Snetchild	int reg;
576150825Snetchild
577150825Snetchild	snd_mtxlock(codec->lock);
578150825Snetchild	codec->count = AC97_INIT(codec->methods, codec->devinfo);
579150825Snetchild	if (codec->count == 0) {
580150825Snetchild		device_printf(codec->dev, "ac97 codec init failed\n");
581150825Snetchild		snd_mtxunlock(codec->lock);
582111679Sorion		return ENODEV;
583111679Sorion	}
584111679Sorion
585111679Sorion	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
586111679Sorion	ac97_reset(codec);
587111679Sorion	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
588111679Sorion
589111679Sorion	i = ac97_rdcd(codec, AC97_REG_RESET);
590111679Sorion	j = ac97_rdcd(codec, AC97_REG_RESET);
591119209Sorion	/*
592111679Sorion	 * Let see if this codec can return consistent value.
593111679Sorion	 * If not, turn on aggressive read workaround
594111679Sorion	 * (STAC9704 comes in mind).
595111679Sorion	 */
596111679Sorion	if (i != j) {
597111679Sorion		codec->flags |= AC97_F_RDCD_BUG;
598111679Sorion		i = ac97_rdcd(codec, AC97_REG_RESET);
599111679Sorion	}
60050724Scg	codec->caps = i & 0x03ff;
60158384Scg	codec->se =  (i & 0x7c00) >> 10;
60250724Scg
603109818Sorion	id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
604111679Sorion	if (id == 0 || id == 0xffffffff) {
605111679Sorion		device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
606111679Sorion		snd_mtxunlock(codec->lock);
607149949Snetchild		return ENODEV;
60850724Scg	}
609149949Snetchild
61050724Scg	codec->id = id;
61174763Scg	codec->noext = 0;
61270134Scg	codec_patch = NULL;
61370134Scg
61470134Scg	cname = NULL;
61574763Scg	model = step = 0;
61670134Scg	for (i = 0; ac97codecid[i].id; i++) {
61770134Scg		u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
61867652Scg		if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
619109818Sorion			codec->noext = ac97codecid[i].noext;
62095499Sorion			codec_patch = ac97codecid[i].patch;
621109818Sorion			cname = ac97codecid[i].name;
62250724Scg			model = (id & modelmask) & 0xff;
623109818Sorion			step = (id & ~modelmask) & 0xff;
624149949Snetchild			break;
625149949Snetchild		}
626149949Snetchild	}
627149949Snetchild
628149949Snetchild	vname = NULL;
629149949Snetchild	for (i = 0; ac97vendorid[i].id; i++) {
630149949Snetchild		if (ac97vendorid[i].id == (id & 0xffffff00)) {
631149949Snetchild			vname = ac97vendorid[i].name;
632149949Snetchild			break;
633149949Snetchild		}
63450724Scg	}
63550724Scg
63650724Scg	codec->extcaps = 0;
637109818Sorion	codec->extid = 0;
63858905Scg	codec->extstat = 0;
63958905Scg	if (!codec->noext) {
64074763Scg		i = ac97_rdcd(codec, AC97_REGEXT_ID);
64158905Scg		if (i != 0xffff) {
64258905Scg			codec->extcaps = i & 0x3fff;
64350724Scg			codec->extid =  (i & 0xc000) >> 14;
644111679Sorion			codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
64565490Scg		}
646109818Sorion	}
647111679Sorion
648111679Sorion	for (i = 0; i < 32; i++) {
649111679Sorion		codec->mix[i] = ac97mixtable_default[i];
65065490Scg	}
651111679Sorion	ac97_fix_auxout(codec);
652111679Sorion	ac97_fix_tone(codec);
65365490Scg	if (codec_patch)
654109818Sorion		codec_patch(codec);
655111679Sorion
656111679Sorion	for (i = 0; i < 32; i++) {
657111679Sorion		k = codec->noext? codec->mix[i].enable : 1;
658111679Sorion		reg = codec->mix[i].reg;
65965490Scg		if (reg < 0)
66065490Scg			reg = -reg;
66158521Scg		if (k && reg) {
662111679Sorion			j = old = ac97_rdcd(codec, reg);
663111679Sorion			/*
664111679Sorion			 * Test for mute bit (except for AC97_MIX_TONE,
665111679Sorion			 * where we simply assume it as available).
666111679Sorion			 */
667111679Sorion			if (codec->mix[i].mute) {
668111679Sorion				ac97_wrcd(codec, reg, j | 0x8000);
669111679Sorion				j = ac97_rdcd(codec, reg);
67070324Scg			} else
67170324Scg				j |= 0x8000;
67270324Scg			if ((j & 0x8000)) {
67370324Scg				/*
674109818Sorion				 * Test whether the control width should be
67570324Scg				 * 4, 5 or 6 bit. For 5bit register, we should
67670324Scg				 * test it whether it's really 5 or 6bit. Leave
67770324Scg				 * 4bit register alone, because sometimes an
678109818Sorion				 * attempt to write past 4th bit may cause
67970324Scg				 * incorrect result especially for AC97_MIX_BEEP
68058521Scg				 * (ac97 2.3).
68158521Scg				 */
68266013Scg				bit = codec->mix[i].bits;
683102302Sorion				if (bit == 5)
684102302Sorion					bit++;
685102302Sorion				j = ((1 << bit) - 1) << codec->mix[i].ofs;
686119209Sorion				ac97_wrcd(codec, reg,
687150825Snetchild					j | (codec->mix[i].mute ? 0x8000 : 0));
688109818Sorion				k = ac97_rdcd(codec, reg) & j;
689109818Sorion				k >>= codec->mix[i].ofs;
690102302Sorion				if (reg == AC97_MIX_TONE &&
691102302Sorion							((k & 0x0001) == 0x0000))
69266307Scg					k >>= 1;
693149949Snetchild				for (j = 0; k >> j; j++)
694149949Snetchild					;
695149949Snetchild				if (j != 0) {
696149949Snetchild#if 0
697149949Snetchild					device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n",
698149949Snetchild						i, k, bit, codec->mix[i].bits, j);
699149949Snetchild#endif
700149949Snetchild					codec->mix[i].enable = 1;
701149949Snetchild					codec->mix[i].bits = j;
702149949Snetchild				} else if (reg == AC97_MIX_BEEP) {
703149949Snetchild					/*
704149949Snetchild					 * Few codec such as CX20468-21 does
705149949Snetchild					 * have this control register, although
706149949Snetchild					 * the only usable part is the mute bit.
707148602Snetchild					 */
708149949Snetchild					codec->mix[i].enable = 1;
709149949Snetchild				} else
710149949Snetchild					codec->mix[i].enable = 0;
711149949Snetchild			} else
712149949Snetchild				codec->mix[i].enable = 0;
713149949Snetchild			ac97_wrcd(codec, reg, old);
714149949Snetchild		}
715149949Snetchild#if 0
716149949Snetchild		printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits);
717149949Snetchild#endif
718149949Snetchild	}
719149949Snetchild
720149949Snetchild	device_printf(codec->dev, "<%s>\n",
721149949Snetchild		      ac97_hw_desc(codec->id, vname, cname, desc));
722149949Snetchild
723149949Snetchild	if (bootverbose) {
724149949Snetchild		if (codec->flags & AC97_F_RDCD_BUG)
725149949Snetchild			device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n");
726149949Snetchild		device_printf(codec->dev, "Codec features ");
727148602Snetchild		for (i = j = 0; i < 10; i++)
728149949Snetchild			if (codec->caps & (1 << i))
729149949Snetchild				printf("%s%s", j++? ", " : "", ac97feature[i]);
730148602Snetchild		printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
731149949Snetchild		printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
732149949Snetchild
733149949Snetchild		if (codec->extcaps != 0 || codec->extid) {
734149949Snetchild			device_printf(codec->dev, "%s codec",
735148602Snetchild				      codec->extid? "Secondary" : "Primary");
736148602Snetchild			if (codec->extcaps)
737153865Sariff				printf(" extended features ");
738153865Sariff			for (i = j = 0; i < 14; i++)
739153865Sariff				if (codec->extcaps & (1 << i))
740153865Sariff					printf("%s%s", j++? ", " : "", ac97extfeature[i]);
741153865Sariff			printf("\n");
742153865Sariff		}
743153865Sariff	}
744148602Snetchild
745148602Snetchild	i = 0;
746148602Snetchild	while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) {
747148602Snetchild		if (++i == 100) {
748149949Snetchild			device_printf(codec->dev, "ac97 codec reports dac not ready\n");
74966013Scg			break;
750148602Snetchild		}
751148602Snetchild		DELAY(1000);
752148602Snetchild	}
75366013Scg	if (bootverbose)
75450724Scg		device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i);
755111679Sorion	snd_mtxunlock(codec->lock);
756111679Sorion	return 0;
757110258Sorion}
75850724Scg
759149949Snetchildstatic unsigned
760149949Snetchildac97_reinitmixer(struct ac97_info *codec)
761150825Snetchild{
762150825Snetchild	snd_mtxlock(codec->lock);
763111679Sorion	codec->count = AC97_INIT(codec->methods, codec->devinfo);
76458384Scg	if (codec->count == 0) {
76558384Scg		device_printf(codec->dev, "ac97 codec init failed\n");
76658384Scg		snd_mtxunlock(codec->lock);
76758384Scg		return ENODEV;
76858384Scg	}
76958384Scg
77058384Scg	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
771111679Sorion	ac97_reset(codec);
772111679Sorion	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
77358384Scg
77458384Scg	if (!codec->noext) {
77558384Scg		ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
77658384Scg		if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
77758384Scg		    != codec->extstat)
77858384Scg			device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
77950724Scg				      codec->extstat,
78050724Scg				      ac97_rdcd(codec, AC97_REGEXT_STAT) &
78150724Scg				      AC97_EXTCAPS);
782148602Snetchild	}
783148602Snetchild
784148602Snetchild	if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
785148602Snetchild		device_printf(codec->dev, "ac97 codec reports dac not ready\n");
786148602Snetchild	snd_mtxunlock(codec->lock);
787148602Snetchild	return 0;
788148602Snetchild}
789148602Snetchild
790148602Snetchildstruct ac97_info *
791148602Snetchildac97_create(device_t dev, void *devinfo, kobj_class_t cls)
79274763Scg{
79350724Scg	struct ac97_info *codec;
79450724Scg	int eapd_inv;
79550724Scg
79667652Scg	codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT | M_ZERO);
79767652Scg	if (codec == NULL)
79867652Scg		return NULL;
79974763Scg
80070134Scg	snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
80170134Scg	codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
80270134Scg	codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO);
80374763Scg	if (codec->methods == NULL) {
80470134Scg		snd_mtxlock(codec->lock);
80570134Scg		snd_mtxfree(codec->lock);
80667652Scg		free(codec, M_AC97);
807109818Sorion		return NULL;
80895499Sorion	}
809109818Sorion
81067652Scg	codec->dev = dev;
81167652Scg	codec->devinfo = devinfo;
812109818Sorion	codec->flags = 0;
813109818Sorion	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
81487623Sguido		    "ac97_eapd_inv", &eapd_inv) == 0) {
81567652Scg		if (eapd_inv != 0)
81687623Sguido			codec->flags |= AC97_F_EAPD_INV;
817109818Sorion	}
818109818Sorion	return codec;
81967652Scg}
82067652Scg
821109818Sorionvoid
82267652Scgac97_destroy(struct ac97_info *codec)
82374763Scg{
82467652Scg	snd_mtxlock(codec->lock);
82567652Scg	if (codec->methods != NULL)
82667652Scg		kobj_delete(codec->methods, M_AC97);
82750724Scg	snd_mtxfree(codec->lock);
82870134Scg	free(codec, M_AC97);
82950724Scg}
83050724Scg
83150724Scgvoid
83270134Scgac97_setflags(struct ac97_info *codec, u_int32_t val)
83370134Scg{
83470134Scg	codec->flags = val;
83570134Scg}
83674763Scg
83793816Sjhbu_int32_t
838111119Simpac97_getflags(struct ac97_info *codec)
83970134Scg{
84074763Scg	return codec->flags;
84174763Scg}
84270134Scg
84370134Scg/* -------------------------------------------------------------------- */
84450724Scg
84570134Scgstatic int
84670134Scgac97mix_init(struct snd_mixer *m)
84770134Scg{
84878668Scg	struct ac97_info *codec = mix_getdevinfo(m);
84950724Scg	struct snddev_info *d;
85050724Scg	u_int32_t i, mask;
85150724Scg
85265340Scg	if (codec == NULL)
85365340Scg		return -1;
85465340Scg
85574763Scg	if (ac97_initmixer(codec))
85670134Scg		return -1;
85770134Scg
85874763Scg	switch (codec->id) {
85970134Scg	case 0x41445374:	/* AD1981B */
86065340Scg#if 0
86165340Scg		mask = 0;
86278668Scg		if (codec->mix[SOUND_MIXER_OGAIN].enable)
86378668Scg			mask |= SOUND_MASK_OGAIN;
86478668Scg		if (codec->mix[SOUND_MIXER_PHONEOUT].enable)
86578668Scg			mask |= SOUND_MASK_PHONEOUT;
86678668Scg		/* Tie ogain/phone to master volume */
86778668Scg		if (codec->mix[SOUND_MIXER_VOLUME].enable)
86878668Scg			mix_setparentchild(m, SOUND_MIXER_VOLUME, mask);
86978668Scg		else {
87078668Scg			mix_setparentchild(m, SOUND_MIXER_VOLUME, mask);
87178668Scg			mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
87278668Scg		}
87378668Scg#endif
87470134Scg		break;
87570134Scg	case 0x434d4941:	/* CMI9738 */
87650724Scg	case 0x434d4961:	/* CMI9739 */
87774763Scg	case 0x434d4978:	/* CMI9761 */
87850724Scg	case 0x434d4982:	/* CMI9761 */
87950724Scg	case 0x434d4983:	/* CMI9761 */
88066013Scg		ac97_wrcd(codec, AC97_MIX_PCM, 0);
88166013Scg		bzero(&codec->mix[SOUND_MIXER_PCM],
88258384Scg		    sizeof(codec->mix[SOUND_MIXER_PCM]));
88358384Scg		d = device_get_softc(codec->dev);
88466013Scg		if (d != NULL)
88558905Scg			d->flags |= SD_F_SOFTPCMVOL;
88658905Scg		/* XXX How about master volume ? */
88766013Scg		break;
88866013Scg	default:
88966013Scg		break;
89066013Scg	}
89166013Scg
89266013Scg#if 0
89366013Scg	/* XXX For the sake of debugging purposes */
89466013Scg	mix_setparentchild(m, SOUND_MIXER_VOLUME,
89566013Scg	    SOUND_MASK_PCM | SOUND_MASK_CD);
89666013Scg	mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
89750724Scg	ac97_wrcd(codec, AC97_MIX_MASTER, 0);
89850724Scg#endif
89950724Scg
90050724Scg	mask = 0;
90174763Scg	for (i = 0; i < 32; i++)
90265340Scg		mask |= codec->mix[i].enable? 1 << i : 0;
90365340Scg	mix_setdevs(m, mask);
90466013Scg
90565340Scg	mask = 0;
90665340Scg	for (i = 0; i < 32; i++)
90765340Scg		mask |= codec->mix[i].recidx? 1 << i : 0;
90865340Scg	mix_setrecdevs(m, mask);
90965340Scg	return 0;
91065340Scg}
91165340Scg
91265340Scgstatic int
91365340Scgac97mix_uninit(struct snd_mixer *m)
91465340Scg{
91565340Scg	struct ac97_info *codec = mix_getdevinfo(m);
91674763Scg
91767652Scg	if (codec == NULL)
91867652Scg		return -1;
91967652Scg	/*
92067652Scg	if (ac97_uninitmixer(codec))
92167652Scg		return -1;
92267652Scg	*/
92367652Scg	ac97_destroy(codec);
92467652Scg	return 0;
92567652Scg}
92674763Scg
92750724Scgstatic int
92850724Scgac97mix_reinit(struct snd_mixer *m)
92966013Scg{
93058384Scg	struct ac97_info *codec = mix_getdevinfo(m);
93158384Scg
93250724Scg	if (codec == NULL)
93350724Scg		return -1;
93450724Scg	return ac97_reinitmixer(codec);
93550724Scg}
93674763Scg
93750724Scgstatic int
93850724Scgac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
93950724Scg{
94066013Scg	struct ac97_info *codec = mix_getdevinfo(m);
94158384Scg
94258384Scg	if (codec == NULL)
94350724Scg		return -1;
94458384Scg	return ac97_setmixer(codec, dev, left, right);
94558384Scg}
94650724Scg
94750724Scgstatic int
94850724Scgac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
94970134Scg{
95070134Scg	int i;
95170134Scg	struct ac97_info *codec = mix_getdevinfo(m);
95270134Scg
95370134Scg	if (codec == NULL)
95470134Scg		return -1;
95570134Scg	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
95650724Scg		if ((src & (1 << i)) != 0)
95770134Scg			break;
95850724Scg	return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
95970134Scg}
96070134Scg
96170134Scgstatic kobj_method_t ac97mixer_methods[] = {
96270134Scg    	KOBJMETHOD(mixer_init,		ac97mix_init),
96370134Scg    	KOBJMETHOD(mixer_uninit,	ac97mix_uninit),
96470134Scg    	KOBJMETHOD(mixer_reinit,	ac97mix_reinit),
96570134Scg    	KOBJMETHOD(mixer_set,		ac97mix_set),
96670134Scg    	KOBJMETHOD(mixer_setrecsrc,	ac97mix_setrecsrc),
96770134Scg	{ 0, 0 }
968};
969MIXER_DECLARE(ac97mixer);
970
971/* -------------------------------------------------------------------- */
972
973kobj_class_t
974ac97_getmixerclass(void)
975{
976	return &ac97mixer_class;
977}
978
979
980