1 /* 2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 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. 13 * 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 24 * SUCH DAMAGE. 25 */ 26 27 #include <dev/sound/pcm/sound.h> 28 #include <dev/sound/pcm/ac97.h> 29 30 #include "mixer_if.h" 31 32 SND_DECLARE_FILE("$FreeBSD$"); 33 34 MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); 35 36 struct ac97mixtable_entry { 37 int reg:8; 38 unsigned bits:4; 39 unsigned ofs:4; 40 unsigned stereo:1; 41 unsigned mute:1; 42 unsigned recidx:4; 43 unsigned mask:1; 44 unsigned enable:1; 45 }; 46 47 #define AC97_NAMELEN 16 48 struct ac97_info { 49 kobj_t methods; 50 device_t dev; 51 void *devinfo; 52 char *id; 53 char rev; 54 unsigned count, caps, se, extcaps, extid, extstat, noext:1; 55 u_int32_t flags; 56 struct ac97mixtable_entry mix[32]; 57 char name[AC97_NAMELEN]; 58 void *lock; 59 }; 60 61 struct ac97_codecid { 62 u_int32_t id, noext:1; 63 char *name; 64 }; 65 66 static const struct ac97mixtable_entry ac97mixtable_default[32] = { 67 [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, 68 [SOUND_MIXER_MONITOR] = { AC97_MIX_PHONES, 5, 0, 1, 1, 0, 0, 0 }, 69 [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, 70 [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, 71 [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, 72 [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, 73 [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, 74 [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, 75 [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, 76 [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0, 1 }, 77 [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 78 [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 79 [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 80 [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 81 }; 82 83 static struct ac97_codecid ac97codecid[] = { 84 { 0x41445303, 0, "Analog Devices AD1819" }, 85 { 0x41445340, 0, "Analog Devices AD1881" }, 86 { 0x41445348, 0, "Analog Devices AD1881A" }, 87 { 0x41445360, 0, "Analog Devices AD1885" }, 88 { 0x414b4d00, 1, "Asahi Kasei AK4540" }, 89 { 0x414b4d01, 1, "Asahi Kasei AK4542" }, 90 { 0x414b4d02, 1, "Asahi Kasei AK4543" }, 91 { 0x414c4710, 0, "Avance Logic ALC200/200P" }, 92 { 0x43525900, 0, "Cirrus Logic CS4297" }, 93 { 0x43525903, 0, "Cirrus Logic CS4297" }, 94 { 0x43525913, 0, "Cirrus Logic CS4297A" }, 95 { 0x43525914, 0, "Cirrus Logic CS4297B" }, 96 { 0x43525923, 0, "Cirrus Logic CS4294C" }, 97 { 0x4352592b, 0, "Cirrus Logic CS4298C" }, 98 { 0x43525931, 0, "Cirrus Logic CS4299A" }, 99 { 0x43525933, 0, "Cirrus Logic CS4299C" }, 100 { 0x43525934, 0, "Cirrus Logic CS4299D" }, 101 { 0x43525941, 0, "Cirrus Logic CS4201A" }, 102 { 0x43525951, 0, "Cirrus Logic CS4205A" }, 103 { 0x43525961, 0, "Cirrus Logic CS4291A" }, 104 { 0x45838308, 0, "ESS Technology ES1921" }, 105 { 0x49434511, 0, "ICEnsemble ICE1232" }, 106 { 0x4e534331, 0, "National Semiconductor LM4549" }, 107 { 0x83847600, 0, "SigmaTel STAC9700/9783/9784" }, 108 { 0x83847604, 0, "SigmaTel STAC9701/9703/9704/9705" }, 109 { 0x83847605, 0, "SigmaTel STAC9704" }, 110 { 0x83847608, 0, "SigmaTel STAC9708/9711" }, 111 { 0x83847609, 0, "SigmaTel STAC9721/9723" }, 112 { 0x83847644, 0, "SigmaTel STAC9744" }, 113 { 0x83847656, 0, "SigmaTel STAC9756/9757" }, 114 { 0x53494c22, 0, "Silicon Laboratory Si3036" }, 115 { 0x53494c23, 0, "Silicon Laboratory Si3038" }, 116 { 0x54524103, 0, "TriTech TR?????" }, 117 { 0x54524106, 0, "TriTech TR28026" }, 118 { 0x54524108, 0, "TriTech TR28028" }, 119 { 0x54524123, 0, "TriTech TR28602" }, 120 { 0x574d4c00, 0, "Wolfson WM9701A" }, 121 { 0x574d4c03, 0, "Wolfson WM9703/9704" }, 122 { 0x574d4c04, 0, "Wolfson WM9704 (quad)" }, 123 { 0, 0, NULL } 124 }; 125 126 static char *ac97enhancement[] = { 127 "no 3D Stereo Enhancement", 128 "Analog Devices Phat Stereo", 129 "Creative Stereo Enhancement", 130 "National Semi 3D Stereo Enhancement", 131 "Yamaha Ymersion", 132 "BBE 3D Stereo Enhancement", 133 "Crystal Semi 3D Stereo Enhancement", 134 "Qsound QXpander", 135 "Spatializer 3D Stereo Enhancement", 136 "SRS 3D Stereo Enhancement", 137 "Platform Tech 3D Stereo Enhancement", 138 "AKM 3D Audio", 139 "Aureal Stereo Enhancement", 140 "Aztech 3D Enhancement", 141 "Binaura 3D Audio Enhancement", 142 "ESS Technology Stereo Enhancement", 143 "Harman International VMAx", 144 "Nvidea 3D Stereo Enhancement", 145 "Philips Incredible Sound", 146 "Texas Instruments 3D Stereo Enhancement", 147 "VLSI Technology 3D Stereo Enhancement", 148 "TriTech 3D Stereo Enhancement", 149 "Realtek 3D Stereo Enhancement", 150 "Samsung 3D Stereo Enhancement", 151 "Wolfson Microelectronics 3D Enhancement", 152 "Delta Integration 3D Enhancement", 153 "SigmaTel 3D Enhancement", 154 "Reserved 27", 155 "Rockwell 3D Stereo Enhancement", 156 "Reserved 29", 157 "Reserved 30", 158 "Reserved 31" 159 }; 160 161 static char *ac97feature[] = { 162 "mic channel", 163 "reserved", 164 "tone", 165 "simulated stereo", 166 "headphone", 167 "bass boost", 168 "18 bit DAC", 169 "20 bit DAC", 170 "18 bit ADC", 171 "20 bit ADC" 172 }; 173 174 static char *ac97extfeature[] = { 175 "variable rate PCM", 176 "double rate PCM", 177 "reserved 1", 178 "variable rate mic", 179 "reserved 2", 180 "reserved 3", 181 "center DAC", 182 "surround DAC", 183 "LFE DAC", 184 "AMAP", 185 "reserved 4", 186 "reserved 5", 187 "reserved 6", 188 "reserved 7", 189 }; 190 191 static u_int16_t 192 rdcd(struct ac97_info *codec, int reg) 193 { 194 return AC97_READ(codec->methods, codec->devinfo, reg); 195 } 196 197 static void 198 wrcd(struct ac97_info *codec, int reg, u_int16_t val) 199 { 200 AC97_WRITE(codec->methods, codec->devinfo, reg, val); 201 } 202 203 int 204 ac97_setrate(struct ac97_info *codec, int which, int rate) 205 { 206 u_int16_t v; 207 208 switch(which) { 209 case AC97_REGEXT_FDACRATE: 210 case AC97_REGEXT_SDACRATE: 211 case AC97_REGEXT_LDACRATE: 212 case AC97_REGEXT_LADCRATE: 213 case AC97_REGEXT_MADCRATE: 214 break; 215 216 default: 217 return -1; 218 } 219 220 snd_mtxlock(codec->lock); 221 if (rate != 0) { 222 v = rate; 223 if (codec->extstat & AC97_EXTCAP_DRA) 224 v >>= 1; 225 wrcd(codec, which, v); 226 } 227 v = rdcd(codec, which); 228 if (codec->extstat & AC97_EXTCAP_DRA) 229 v <<= 1; 230 snd_mtxunlock(codec->lock); 231 return v; 232 } 233 234 int 235 ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 236 { 237 mode &= AC97_EXTCAPS; 238 if ((mode & ~codec->extcaps) != 0) 239 return -1; 240 snd_mtxlock(codec->lock); 241 wrcd(codec, AC97_REGEXT_STAT, mode); 242 codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 243 snd_mtxunlock(codec->lock); 244 return (mode == codec->extstat)? 0 : -1; 245 } 246 247 u_int16_t 248 ac97_getextmode(struct ac97_info *codec) 249 { 250 return codec->extstat; 251 } 252 253 u_int16_t 254 ac97_getextcaps(struct ac97_info *codec) 255 { 256 return codec->extcaps; 257 } 258 259 u_int16_t 260 ac97_getcaps(struct ac97_info *codec) 261 { 262 return codec->caps; 263 } 264 265 static int 266 ac97_setrecsrc(struct ac97_info *codec, int channel) 267 { 268 struct ac97mixtable_entry *e = &codec->mix[channel]; 269 270 if (e->recidx > 0) { 271 int val = e->recidx - 1; 272 val |= val << 8; 273 snd_mtxlock(codec->lock); 274 wrcd(codec, AC97_REG_RECSEL, val); 275 snd_mtxunlock(codec->lock); 276 return 0; 277 } else 278 return -1; 279 } 280 281 static int 282 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 283 { 284 struct ac97mixtable_entry *e = &codec->mix[channel]; 285 286 if (e->reg && e->enable && e->bits) { 287 int max, val, reg = (e->reg >= 0)? e->reg : -e->reg; 288 289 if (!e->stereo) 290 right = left; 291 if (e->reg > 0) { 292 left = 100 - left; 293 right = 100 - right; 294 } 295 296 max = (1 << e->bits) - 1; 297 left = (left * max) / 100; 298 right = (right * max) / 100; 299 300 val = (left << 8) | right; 301 302 left = (left * 100) / max; 303 right = (right * 100) / max; 304 305 if (e->reg > 0) { 306 left = 100 - left; 307 right = 100 - right; 308 } 309 310 if (!e->stereo) { 311 val &= max; 312 val <<= e->ofs; 313 if (e->mask) { 314 int cur = rdcd(codec, e->reg); 315 val |= cur & ~(max << e->ofs); 316 } 317 } 318 if (left == 0 && right == 0 && e->mute == 1) 319 val = AC97_MUTE; 320 snd_mtxlock(codec->lock); 321 wrcd(codec, reg, val); 322 snd_mtxunlock(codec->lock); 323 return left | (right << 8); 324 } else { 325 /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */ 326 return -1; 327 } 328 } 329 330 #if 0 331 static int 332 ac97_getmixer(struct ac97_info *codec, int channel) 333 { 334 struct ac97mixtable_entry *e = &codec->mix[channel]; 335 if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 336 int max, val, volume; 337 338 max = (1 << e->bits) - 1; 339 val = rdcd(code, e->reg); 340 if (val == AC97_MUTE && e->mute == 1) 341 volume = 0; 342 else { 343 if (e->stereo == 0) val >>= e->ofs; 344 val &= max; 345 volume = (val * 100) / max; 346 if (e->reg > 0) volume = 100 - volume; 347 } 348 return volume; 349 } else 350 return -1; 351 } 352 #endif 353 354 static unsigned 355 ac97_initmixer(struct ac97_info *codec) 356 { 357 unsigned i, j, k, old; 358 u_int32_t id; 359 360 snd_mtxlock(codec->lock); 361 for (i = 0; i < 32; i++) 362 codec->mix[i] = ac97mixtable_default[i]; 363 364 codec->count = AC97_INIT(codec->methods, codec->devinfo); 365 if (codec->count == 0) { 366 device_printf(codec->dev, "ac97 codec init failed\n"); 367 snd_mtxunlock(codec->lock); 368 return ENODEV; 369 } 370 371 wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 372 wrcd(codec, AC97_REG_RESET, 0); 373 DELAY(100000); 374 wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 375 376 i = rdcd(codec, AC97_REG_RESET); 377 codec->caps = i & 0x03ff; 378 codec->se = (i & 0x7c00) >> 10; 379 380 id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2); 381 codec->rev = id & 0x000000ff; 382 if (id == 0 || id == 0xffffffff) { 383 device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 384 snd_mtxunlock(codec->lock); 385 return ENODEV; 386 } 387 388 codec->noext = 0; 389 codec->id = NULL; 390 for (i = 0; ac97codecid[i].id; i++) { 391 if (ac97codecid[i].id == id) { 392 codec->id = ac97codecid[i].name; 393 codec->noext = ac97codecid[i].noext; 394 } 395 } 396 397 codec->extcaps = 0; 398 codec->extid = 0; 399 codec->extstat = 0; 400 if (!codec->noext) { 401 i = rdcd(codec, AC97_REGEXT_ID); 402 if (i != 0xffff) { 403 codec->extcaps = i & 0x3fff; 404 codec->extid = (i & 0xc000) >> 14; 405 codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 406 } 407 } 408 409 for (i = 0; i < 32; i++) { 410 k = codec->noext? codec->mix[i].enable : 1; 411 if (k && (codec->mix[i].reg > 0)) { 412 old = rdcd(codec, codec->mix[i].reg); 413 wrcd(codec, codec->mix[i].reg, 0x3f); 414 j = rdcd(codec, codec->mix[i].reg); 415 wrcd(codec, codec->mix[i].reg, old); 416 codec->mix[i].enable = (j != 0 && j != old)? 1 : 0; 417 for (k = 1; j & (1 << k); k++); 418 codec->mix[i].bits = j? k - codec->mix[i].ofs : 0; 419 } 420 /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ 421 } 422 423 if (bootverbose) { 424 device_printf(codec->dev, "ac97 codec id 0x%08x", id); 425 if (codec->id) 426 printf(" (%s)", codec->id); 427 printf("\n"); 428 device_printf(codec->dev, "ac97 codec features "); 429 for (i = j = 0; i < 10; i++) 430 if (codec->caps & (1 << i)) 431 printf("%s%s", j++? ", " : "", ac97feature[i]); 432 printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 433 printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 434 435 if (codec->extcaps != 0 || codec->extid) { 436 device_printf(codec->dev, "ac97 %s codec", 437 codec->extid? "secondary" : "primary"); 438 if (codec->extcaps) 439 printf(" extended features "); 440 for (i = j = 0; i < 14; i++) 441 if (codec->extcaps & (1 << i)) 442 printf("%s%s", j++? ", " : "", ac97extfeature[i]); 443 printf("\n"); 444 } 445 } 446 447 if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 448 device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 449 snd_mtxunlock(codec->lock); 450 return 0; 451 } 452 453 static unsigned 454 ac97_reinitmixer(struct ac97_info *codec) 455 { 456 unsigned i; 457 458 snd_mtxlock(codec->lock); 459 codec->count = AC97_INIT(codec->methods, codec->devinfo); 460 if (codec->count == 0) { 461 device_printf(codec->dev, "ac97 codec init failed\n"); 462 snd_mtxunlock(codec->lock); 463 return ENODEV; 464 } 465 466 wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 467 wrcd(codec, AC97_REG_RESET, 0); 468 DELAY(100000); 469 wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 470 i = rdcd(codec, AC97_REG_RESET); 471 472 if (!codec->noext) { 473 wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 474 if (rdcd(codec, AC97_REGEXT_STAT) != codec->extstat) 475 device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 476 codec->extstat, rdcd(codec, AC97_REGEXT_STAT)); 477 } 478 479 if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 480 device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 481 snd_mtxunlock(codec->lock); 482 return 0; 483 } 484 485 struct ac97_info * 486 ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 487 { 488 struct ac97_info *codec; 489 490 codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); 491 if (codec == NULL) 492 return NULL; 493 494 snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); 495 codec->lock = snd_mtxcreate(codec->name); 496 codec->methods = kobj_create(cls, M_AC97, M_WAITOK); 497 if (codec->methods == NULL) { 498 snd_mtxlock(codec->lock); 499 snd_mtxfree(codec->lock); 500 free(codec, M_AC97); 501 return NULL; 502 } 503 504 codec->dev = dev; 505 codec->devinfo = devinfo; 506 codec->flags = 0; 507 return codec; 508 } 509 510 void 511 ac97_destroy(struct ac97_info *codec) 512 { 513 snd_mtxlock(codec->lock); 514 if (codec->methods != NULL) 515 kobj_delete(codec->methods, M_AC97); 516 snd_mtxfree(codec->lock); 517 free(codec, M_AC97); 518 } 519 520 void 521 ac97_setflags(struct ac97_info *codec, u_int32_t val) 522 { 523 codec->flags = val; 524 } 525 526 u_int32_t 527 ac97_getflags(struct ac97_info *codec) 528 { 529 return codec->flags; 530 } 531 532 /* -------------------------------------------------------------------- */ 533 534 static int 535 ac97mix_init(struct snd_mixer *m) 536 { 537 struct ac97_info *codec = mix_getdevinfo(m); 538 u_int32_t i, mask; 539 540 if (codec == NULL) 541 return -1; 542 543 if (ac97_initmixer(codec)) 544 return -1; 545 546 mask = 0; 547 for (i = 0; i < 32; i++) 548 mask |= codec->mix[i].enable? 1 << i : 0; 549 mix_setdevs(m, mask); 550 551 mask = 0; 552 for (i = 0; i < 32; i++) 553 mask |= codec->mix[i].recidx? 1 << i : 0; 554 mix_setrecdevs(m, mask); 555 return 0; 556 } 557 558 static int 559 ac97mix_uninit(struct snd_mixer *m) 560 { 561 struct ac97_info *codec = mix_getdevinfo(m); 562 563 if (codec == NULL) 564 return -1; 565 /* 566 if (ac97_uninitmixer(codec)) 567 return -1; 568 */ 569 ac97_destroy(codec); 570 return 0; 571 } 572 573 static int 574 ac97mix_reinit(struct snd_mixer *m) 575 { 576 struct ac97_info *codec = mix_getdevinfo(m); 577 578 if (codec == NULL) 579 return -1; 580 return ac97_reinitmixer(codec); 581 } 582 583 static int 584 ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 585 { 586 struct ac97_info *codec = mix_getdevinfo(m); 587 588 if (codec == NULL) 589 return -1; 590 return ac97_setmixer(codec, dev, left, right); 591 } 592 593 static int 594 ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 595 { 596 int i; 597 struct ac97_info *codec = mix_getdevinfo(m); 598 599 if (codec == NULL) 600 return -1; 601 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 602 if ((src & (1 << i)) != 0) 603 break; 604 return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 605 } 606 607 static kobj_method_t ac97mixer_methods[] = { 608 KOBJMETHOD(mixer_init, ac97mix_init), 609 KOBJMETHOD(mixer_uninit, ac97mix_uninit), 610 KOBJMETHOD(mixer_reinit, ac97mix_reinit), 611 KOBJMETHOD(mixer_set, ac97mix_set), 612 KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 613 { 0, 0 } 614 }; 615 MIXER_DECLARE(ac97mixer); 616 617 /* -------------------------------------------------------------------- */ 618 619 kobj_class_t 620 ac97_getmixerclass(void) 621 { 622 return &ac97mixer_class; 623 } 624 625 626