2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * $FreeBSD: src/sys/dev/sound/pcm/ac97.c,v 1.49 2003/11/11 22:15:17 kuriyama Exp $
27 * $DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.3 2004/01/21 20:02:08 asmodai Exp $
30 #include <dev/sound/pcm/sound.h>
31 #include <dev/sound/pcm/ac97.h>
32 #include <dev/sound/pcm/ac97_patch.h>
36 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.3 2004/01/21 20:02:08 asmodai Exp $");
38 MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
40 struct ac97mixtable_entry {
41 int reg:8; /* register index */
42 /* reg < 0 if inverted polarity */
43 unsigned bits:4; /* width of control field */
44 unsigned ofs:4; /* offset (only if stereo=0) */
45 unsigned stereo:1; /* set for stereo controls */
46 unsigned mute:1; /* bit15 is MUTE */
47 unsigned recidx:4; /* index in rec mux */
48 unsigned mask:1; /* use only masked bits */
49 unsigned enable:1; /* entry is enabled */
52 #define AC97_NAMELEN 16
58 unsigned count, caps, se, extcaps, extid, extstat, noext:1;
60 struct ac97mixtable_entry mix[32];
61 char name[AC97_NAMELEN];
65 struct ac97_vendorid {
78 static const struct ac97mixtable_entry ac97mixtable_default[32] = {
79 /* [offset] reg bits of st mu re mk en */
80 [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 },
81 [SOUND_MIXER_OGAIN] = { AC97_MIX_AUXOUT, 5, 0, 1, 1, 0, 0, 0 },
82 [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 },
83 [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 },
84 [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 },
85 [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 },
86 [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 },
87 [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 },
88 [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 },
89 [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 1, 1 },
91 /* use igain for the mic 20dB boost */
92 [SOUND_MIXER_IGAIN] = { -AC97_MIX_MIC, 1, 6, 0, 0, 0, 1, 1 },
94 [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 },
95 [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 },
96 [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 },
97 [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 }
100 static const struct ac97_vendorid ac97vendorid[] = {
101 { 0x41445300, "Analog Devices" },
102 { 0x414b4d00, "Asahi Kasei" },
103 { 0x414c4300, "Realtek" },
104 { 0x414c4700, "Avance Logic" },
105 { 0x43525900, "Cirrus Logic" },
106 { 0x434d4900, "C-Media Electronics" },
107 { 0x43585400, "Conexant" },
108 { 0x454d4300, "eMicro" },
109 { 0x45838300, "ESS Technology" },
110 { 0x49434500, "ICEnsemble" },
111 { 0x4e534300, "National Semiconductor" },
112 { 0x50534300, "Philips Semiconductor" },
113 { 0x83847600, "SigmaTel" },
114 { 0x53494c00, "Silicon Laboratory" },
115 { 0x54524100, "TriTech" },
116 { 0x56494100, "VIA Technologies" },
117 { 0x574d4c00, "Wolfson" },
118 { 0x594d4800, "Yamaha" },
119 { 0x01408300, "Creative" },
123 static struct ac97_codecid ac97codecid[] = {
124 { 0x41445303, 0x00, 0, "AD1819", 0 },
125 { 0x41445340, 0x00, 0, "AD1881", 0 },
126 { 0x41445348, 0x00, 0, "AD1881A", 0 },
127 { 0x41445360, 0x00, 0, "AD1885", 0 },
128 { 0x41445361, 0x00, 0, "AD1886", ad1886_patch },
129 { 0x41445362, 0x00, 0, "AD1887", 0 },
130 { 0x41445363, 0x00, 0, "AD1886A", 0 },
131 { 0x41445370, 0x00, 0, "AD1980", ad198x_patch },
132 { 0x41445372, 0x00, 0, "AD1981A", 0 },
133 { 0x41445374, 0x00, 0, "AD1981B", 0 },
134 { 0x41445375, 0x00, 0, "AD1985", ad198x_patch },
135 { 0x414b4d00, 0x00, 1, "AK4540", 0 },
136 { 0x414b4d01, 0x00, 1, "AK4542", 0 },
137 { 0x414b4d02, 0x00, 1, "AK4543", 0 },
138 { 0x414c4320, 0x0f, 0, "ALC100", 0 },
139 { 0x414c4730, 0x0f, 0, "ALC101", 0 },
140 { 0x414c4710, 0x0f, 0, "ALC200", 0 },
141 { 0x414c4740, 0x0f, 0, "ALC202", 0 },
142 { 0x414c4720, 0x0f, 0, "ALC650", 0 },
143 { 0x414c4760, 0x0f, 0, "ALC655", 0 },
144 { 0x414c4780, 0x0f, 0, "ALC658", 0 },
145 { 0x43525900, 0x07, 0, "CS4297", 0 },
146 { 0x43525910, 0x07, 0, "CS4297A", 0 },
147 { 0x43525920, 0x07, 0, "CS4294/98", 0 },
148 { 0x43525930, 0x07, 0, "CS4299", 0 },
149 { 0x43525940, 0x07, 0, "CS4201", 0 },
150 { 0x43525958, 0x07, 0, "CS4205", 0 },
151 { 0x43525960, 0x07, 0, "CS4291A", 0 },
152 { 0x434d4961, 0x00, 0, "CMI9739", 0 },
153 { 0x434d4941, 0x00, 0, "CMI9738", 0 },
154 { 0x43585429, 0x00, 0, "CX20468", 0 },
155 { 0x454d4323, 0x00, 0, "EM28023", 0 },
156 { 0x454d4328, 0x00, 0, "EM28028", 0 },
157 { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */
158 { 0x49434501, 0x00, 0, "ICE1230", 0 },
159 { 0x49434511, 0x00, 0, "ICE1232", 0 },
160 { 0x49434514, 0x00, 0, "ICE1232A", 0 },
161 { 0x49434551, 0x03, 0, "VT1616", 0 }, /* Via badged ICE */
162 { 0x4e534340, 0x00, 0, "LM4540", 0 }, /* Spec blank on revid */
163 { 0x4e534343, 0x00, 0, "LM4543", 0 }, /* Ditto */
164 { 0x4e534346, 0x00, 0, "LM4546A", 0 },
165 { 0x4e534348, 0x00, 0, "LM4548A", 0 },
166 { 0x4e534331, 0x00, 0, "LM4549", 0 }, /* (?) */
167 { 0x4e534349, 0x00, 0, "LM4549A", 0 },
168 { 0x4e534350, 0x00, 0, "LM4550", 0 },
169 { 0x50534301, 0x00, 0, "UCB1510", 0 },
170 { 0x50534304, 0x00, 0, "UCB1400", 0 },
171 { 0x83847600, 0x00, 0, "STAC9700/83/84", 0 },
172 { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
173 { 0x83847605, 0x00, 0, "STAC9704", 0 },
174 { 0x83847608, 0x00, 0, "STAC9708/11", 0 },
175 { 0x83847609, 0x00, 0, "STAC9721/23", 0 },
176 { 0x83847644, 0x00, 0, "STAC9744/45", 0 },
177 { 0x83847650, 0x00, 0, "STAC9750/51", 0 },
178 { 0x83847652, 0x00, 0, "STAC9752/53", 0 },
179 { 0x83847656, 0x00, 0, "STAC9756/57", 0 },
180 { 0x83847658, 0x00, 0, "STAC9758/59", 0 },
181 { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */
182 { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */
183 { 0x53494c22, 0x00, 0, "Si3036", 0 },
184 { 0x53494c23, 0x00, 0, "Si3038", 0 },
185 { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */
186 { 0x54524106, 0x00, 0, "TR28026", 0 },
187 { 0x54524108, 0x00, 0, "TR28028", 0 },
188 { 0x54524123, 0x00, 0, "TR28602", 0 },
189 { 0x56494161, 0x00, 0, "VIA1612A", 0 },
190 { 0x574d4c00, 0x00, 0, "WM9701A", 0 },
191 { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 },
192 { 0x574d4c04, 0x00, 0, "WM9704Q", 0 },
193 { 0x574d4c05, 0x00, 0, "WM9705/10", 0 },
194 { 0x594d4800, 0x00, 0, "YMF743", 0 },
195 { 0x594d4802, 0x00, 0, "YMF752", 0 },
196 { 0x594d4803, 0x00, 0, "YMF753", 0 },
197 { 0x01408384, 0x00, 0, "EV1938", 0 },
201 static char *ac97enhancement[] = {
202 "no 3D Stereo Enhancement",
203 "Analog Devices Phat Stereo",
204 "Creative Stereo Enhancement",
205 "National Semi 3D Stereo Enhancement",
207 "BBE 3D Stereo Enhancement",
208 "Crystal Semi 3D Stereo Enhancement",
210 "Spatializer 3D Stereo Enhancement",
211 "SRS 3D Stereo Enhancement",
212 "Platform Tech 3D Stereo Enhancement",
214 "Aureal Stereo Enhancement",
215 "Aztech 3D Enhancement",
216 "Binaura 3D Audio Enhancement",
217 "ESS Technology Stereo Enhancement",
218 "Harman International VMAx",
219 "Nvidea 3D Stereo Enhancement",
220 "Philips Incredible Sound",
221 "Texas Instruments 3D Stereo Enhancement",
222 "VLSI Technology 3D Stereo Enhancement",
223 "TriTech 3D Stereo Enhancement",
224 "Realtek 3D Stereo Enhancement",
225 "Samsung 3D Stereo Enhancement",
226 "Wolfson Microelectronics 3D Enhancement",
227 "Delta Integration 3D Enhancement",
228 "SigmaTel 3D Enhancement",
230 "Rockwell 3D Stereo Enhancement",
236 static char *ac97feature[] = {
249 static char *ac97extfeature[] = {
267 ac97_rdcd(struct ac97_info *codec, int reg)
269 return AC97_READ(codec->methods, codec->devinfo, reg);
273 ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
275 AC97_WRITE(codec->methods, codec->devinfo, reg, val);
279 ac97_reset(struct ac97_info *codec)
282 ac97_wrcd(codec, AC97_REG_RESET, 0);
283 for (i = 0; i < 500; i++) {
284 ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
285 if (ps == AC97_POWER_STATUS)
289 device_printf(codec->dev, "AC97 reset timed out.\n");
293 ac97_setrate(struct ac97_info *codec, int which, int rate)
298 case AC97_REGEXT_FDACRATE:
299 case AC97_REGEXT_SDACRATE:
300 case AC97_REGEXT_LDACRATE:
301 case AC97_REGEXT_LADCRATE:
302 case AC97_REGEXT_MADCRATE:
309 snd_mtxlock(codec->lock);
312 if (codec->extstat & AC97_EXTCAP_DRA)
314 ac97_wrcd(codec, which, v);
316 v = ac97_rdcd(codec, which);
317 if (codec->extstat & AC97_EXTCAP_DRA)
319 snd_mtxunlock(codec->lock);
324 ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
326 mode &= AC97_EXTCAPS;
327 if ((mode & ~codec->extcaps) != 0) {
328 device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
332 snd_mtxlock(codec->lock);
333 ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
334 codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
335 snd_mtxunlock(codec->lock);
336 return (mode == codec->extstat)? 0 : -1;
340 ac97_getextmode(struct ac97_info *codec)
342 return codec->extstat;
346 ac97_getextcaps(struct ac97_info *codec)
348 return codec->extcaps;
352 ac97_getcaps(struct ac97_info *codec)
358 ac97_setrecsrc(struct ac97_info *codec, int channel)
360 struct ac97mixtable_entry *e = &codec->mix[channel];
363 int val = e->recidx - 1;
365 snd_mtxlock(codec->lock);
366 ac97_wrcd(codec, AC97_REG_RECSEL, val);
367 snd_mtxunlock(codec->lock);
374 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
376 struct ac97mixtable_entry *e = &codec->mix[channel];
378 if (e->reg && e->enable && e->bits) {
379 int mask, max, val, reg;
381 reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register */
382 max = (1 << e->bits) - 1; /* actual range */
383 mask = (max << 8) | max; /* bits of interest */
389 * Invert the range if the polarity requires so,
390 * then scale to 0..max-1 to compute the value to
391 * write into the codec, and scale back to 0..100
392 * for the return value.
399 left = (left * max) / 100;
400 right = (right * max) / 100;
402 val = (left << 8) | right;
404 left = (left * 100) / max;
405 right = (right * 100) / max;
413 * For mono controls, trim val and mask, also taking
414 * care of e->ofs (offset of control field).
419 mask = (max << e->ofs);
423 * If we have a mute bit, add it to the mask and
424 * update val and set mute if both channels require a
429 if (left == 0 && right == 0)
434 * If the mask bit is set, do not alter the other bits.
436 snd_mtxlock(codec->lock);
438 int cur = ac97_rdcd(codec, e->reg);
439 val |= cur & ~(mask);
441 ac97_wrcd(codec, reg, val);
442 snd_mtxunlock(codec->lock);
443 return left | (right << 8);
445 /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
451 ac97_fix_auxout(struct ac97_info *codec)
456 * By default, The ac97 aux_out register (0x04) corresponds to OSS's
459 * We first check whether aux_out is a valid register. If not
460 * we may not want to keep ogain.
462 keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000;
465 * Determine what AUX_OUT really means, it can be:
469 * 3. True line level out (effectively master volume).
471 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
473 if (codec->extcaps & AC97_EXTCAP_SDAC &&
474 ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
475 codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND;
479 if (keep_ogain == 0) {
480 bzero(&codec->mix[SOUND_MIXER_OGAIN],
481 sizeof(codec->mix[SOUND_MIXER_OGAIN]));
486 ac97_fix_tone(struct ac97_info *codec)
488 /* Hide treble and bass if they don't exist */
489 if ((codec->caps & AC97_CAP_TONE) == 0) {
490 bzero(&codec->mix[SOUND_MIXER_BASS],
491 sizeof(codec->mix[SOUND_MIXER_BASS]));
492 bzero(&codec->mix[SOUND_MIXER_TREBLE],
493 sizeof(codec->mix[SOUND_MIXER_TREBLE]));
498 ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
501 sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
505 if (vname == NULL) vname = "Unknown";
508 sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
510 sprintf(buf, "%s %s AC97 Codec", vname, cname);
516 ac97_initmixer(struct ac97_info *codec)
518 ac97_patch codec_patch;
519 const char *cname, *vname;
521 u_int8_t model, step;
522 unsigned i, j, k, old;
525 snd_mtxlock(codec->lock);
526 codec->count = AC97_INIT(codec->methods, codec->devinfo);
527 if (codec->count == 0) {
528 device_printf(codec->dev, "ac97 codec init failed\n");
529 snd_mtxunlock(codec->lock);
533 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
535 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
537 i = ac97_rdcd(codec, AC97_REG_RESET);
538 codec->caps = i & 0x03ff;
539 codec->se = (i & 0x7c00) >> 10;
541 id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
542 if (id == 0 || id == 0xffffffff) {
543 device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
544 snd_mtxunlock(codec->lock);
554 for (i = 0; ac97codecid[i].id; i++) {
555 u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
556 if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
557 codec->noext = ac97codecid[i].noext;
558 codec_patch = ac97codecid[i].patch;
559 cname = ac97codecid[i].name;
560 model = (id & modelmask) & 0xff;
561 step = (id & ~modelmask) & 0xff;
567 for (i = 0; ac97vendorid[i].id; i++) {
568 if (ac97vendorid[i].id == (id & 0xffffff00)) {
569 vname = ac97vendorid[i].name;
578 i = ac97_rdcd(codec, AC97_REGEXT_ID);
580 codec->extcaps = i & 0x3fff;
581 codec->extid = (i & 0xc000) >> 14;
582 codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
586 for (i = 0; i < 32; i++) {
587 codec->mix[i] = ac97mixtable_default[i];
589 ac97_fix_auxout(codec);
590 ac97_fix_tone(codec);
594 for (i = 0; i < 32; i++) {
595 k = codec->noext? codec->mix[i].enable : 1;
596 if (k && (codec->mix[i].reg > 0)) {
597 old = ac97_rdcd(codec, codec->mix[i].reg);
598 ac97_wrcd(codec, codec->mix[i].reg, 0x3f);
599 j = ac97_rdcd(codec, codec->mix[i].reg);
600 ac97_wrcd(codec, codec->mix[i].reg, old);
601 codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
602 for (k = 1; j & (1 << k); k++);
603 codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
605 /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
608 device_printf(codec->dev, "<%s>\n",
609 ac97_hw_desc(codec->id, vname, cname, desc));
612 device_printf(codec->dev, "Codec features ");
613 for (i = j = 0; i < 10; i++)
614 if (codec->caps & (1 << i))
615 printf("%s%s", j++? ", " : "", ac97feature[i]);
616 printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
617 printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
619 if (codec->extcaps != 0 || codec->extid) {
620 device_printf(codec->dev, "%s codec",
621 codec->extid? "Secondary" : "Primary");
623 printf(" extended features ");
624 for (i = j = 0; i < 14; i++)
625 if (codec->extcaps & (1 << i))
626 printf("%s%s", j++? ", " : "", ac97extfeature[i]);
631 if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
632 device_printf(codec->dev, "ac97 codec reports dac not ready\n");
633 snd_mtxunlock(codec->lock);
638 ac97_reinitmixer(struct ac97_info *codec)
640 snd_mtxlock(codec->lock);
641 codec->count = AC97_INIT(codec->methods, codec->devinfo);
642 if (codec->count == 0) {
643 device_printf(codec->dev, "ac97 codec init failed\n");
644 snd_mtxunlock(codec->lock);
648 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
650 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
653 ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
654 if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
656 device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
658 ac97_rdcd(codec, AC97_REGEXT_STAT) &
662 if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
663 device_printf(codec->dev, "ac97 codec reports dac not ready\n");
664 snd_mtxunlock(codec->lock);
669 ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
671 struct ac97_info *codec;
673 codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
677 snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
678 codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
679 codec->methods = kobj_create(cls, M_AC97, 0);
680 if (codec->methods == NULL) {
681 snd_mtxlock(codec->lock);
682 snd_mtxfree(codec->lock);
688 codec->devinfo = devinfo;
694 ac97_destroy(struct ac97_info *codec)
696 snd_mtxlock(codec->lock);
697 if (codec->methods != NULL)
698 kobj_delete(codec->methods, M_AC97);
699 snd_mtxfree(codec->lock);
704 ac97_setflags(struct ac97_info *codec, u_int32_t val)
710 ac97_getflags(struct ac97_info *codec)
715 /* -------------------------------------------------------------------- */
718 ac97mix_init(struct snd_mixer *m)
720 struct ac97_info *codec = mix_getdevinfo(m);
726 if (ac97_initmixer(codec))
730 for (i = 0; i < 32; i++)
731 mask |= codec->mix[i].enable? 1 << i : 0;
732 mix_setdevs(m, mask);
735 for (i = 0; i < 32; i++)
736 mask |= codec->mix[i].recidx? 1 << i : 0;
737 mix_setrecdevs(m, mask);
742 ac97mix_uninit(struct snd_mixer *m)
744 struct ac97_info *codec = mix_getdevinfo(m);
749 if (ac97_uninitmixer(codec))
757 ac97mix_reinit(struct snd_mixer *m)
759 struct ac97_info *codec = mix_getdevinfo(m);
763 return ac97_reinitmixer(codec);
767 ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
769 struct ac97_info *codec = mix_getdevinfo(m);
773 return ac97_setmixer(codec, dev, left, right);
777 ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
780 struct ac97_info *codec = mix_getdevinfo(m);
784 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
785 if ((src & (1 << i)) != 0)
787 return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
790 static kobj_method_t ac97mixer_methods[] = {
791 KOBJMETHOD(mixer_init, ac97mix_init),
792 KOBJMETHOD(mixer_uninit, ac97mix_uninit),
793 KOBJMETHOD(mixer_reinit, ac97mix_reinit),
794 KOBJMETHOD(mixer_set, ac97mix_set),
795 KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc),
798 MIXER_DECLARE(ac97mixer);
800 /* -------------------------------------------------------------------- */
803 ac97_getmixerclass(void)
805 return &ac97mixer_class;