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 static int 260 ac97_setrecsrc(struct ac97_info *codec, int channel) 261 { 262 struct ac97mixtable_entry *e = &codec->mix[channel]; 263 264 if (e->recidx > 0) { 265 int val = e->recidx - 1; 266 val |= val << 8; 267 snd_mtxlock(codec->lock); 268 wrcd(codec, AC97_REG_RECSEL, val); 269 snd_mtxunlock(codec->lock); 270 return 0; 271 } else 272 return -1; 273 } 274 275 static int 276 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 277 { 278 struct ac97mixtable_entry *e = &codec->mix[channel]; 279 280 if (e->reg && e->enable && e->bits) { 281 int max, val, reg = (e->reg >= 0)? e->reg : -e->reg; 282 283 if (!e->stereo) 284 right = left; 285 if (e->reg > 0) { 286 left = 100 - left; 287 right = 100 - right; 288 } 289 290 max = (1 << e->bits) - 1; 291 left = (left * max) / 100; 292 right = (right * max) / 100; 293 294 val = (left << 8) | right; 295 296 left = (left * 100) / max; 297 right = (right * 100) / max; 298 299 if (e->reg > 0) { 300 left = 100 - left; 301 right = 100 - right; 302 } 303 304 if (!e->stereo) { 305 val &= max; 306 val <<= e->ofs; 307 if (e->mask) { 308 int cur = rdcd(codec, e->reg); 309 val |= cur & ~(max << e->ofs); 310 } 311 } 312 if (left == 0 && right == 0 && e->mute == 1) 313 val = AC97_MUTE; 314 snd_mtxlock(codec->lock); 315 wrcd(codec, reg, val); 316 snd_mtxunlock(codec->lock); 317 return left | (right << 8); 318 } else { 319 /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */ 320 return -1; 321 } 322 } 323 324 #if 0 325 static int 326 ac97_getmixer(struct ac97_info *codec, int channel) 327 { 328 struct ac97mixtable_entry *e = &codec->mix[channel]; 329 if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 330 int max, val, volume; 331 332 max = (1 << e->bits) - 1; 333 val = rdcd(code, e->reg); 334 if (val == AC97_MUTE && e->mute == 1) 335 volume = 0; 336 else { 337 if (e->stereo == 0) val >>= e->ofs; 338 val &= max; 339 volume = (val * 100) / max; 340 if (e->reg > 0) volume = 100 - volume; 341 } 342 return volume; 343 } else 344 return -1; 345 } 346 #endif 347 348 static unsigned 349 ac97_initmixer(struct ac97_info *codec) 350 { 351 unsigned i, j, k, old; 352 u_int32_t id; 353 354 snd_mtxlock(codec->lock); 355 for (i = 0; i < 32; i++) 356 codec->mix[i] = ac97mixtable_default[i]; 357 358 codec->count = AC97_INIT(codec->methods, codec->devinfo); 359 if (codec->count == 0) { 360 device_printf(codec->dev, "ac97 codec init failed\n"); 361 snd_mtxunlock(codec->lock); 362 return ENODEV; 363 } 364 365 wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 366 wrcd(codec, AC97_REG_RESET, 0); 367 DELAY(100000); 368 wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 369 370 i = rdcd(codec, AC97_REG_RESET); 371 codec->caps = i & 0x03ff; 372 codec->se = (i & 0x7c00) >> 10; 373 374 id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2); 375 codec->rev = id & 0x000000ff; 376 if (id == 0 || id == 0xffffffff) { 377 device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 378 snd_mtxunlock(codec->lock); 379 return ENODEV; 380 } 381 382 codec->noext = 0; 383 codec->id = NULL; 384 for (i = 0; ac97codecid[i].id; i++) { 385 if (ac97codecid[i].id == id) { 386 codec->id = ac97codecid[i].name; 387 codec->noext = ac97codecid[i].noext; 388 } 389 } 390 391 codec->extcaps = 0; 392 codec->extid = 0; 393 codec->extstat = 0; 394 if (!codec->noext) { 395 i = rdcd(codec, AC97_REGEXT_ID); 396 if (i != 0xffff) { 397 codec->extcaps = i & 0x3fff; 398 codec->extid = (i & 0xc000) >> 14; 399 codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 400 } 401 } 402 403 for (i = 0; i < 32; i++) { 404 k = codec->noext? codec->mix[i].enable : 1; 405 if (k && (codec->mix[i].reg > 0)) { 406 old = rdcd(codec, codec->mix[i].reg); 407 wrcd(codec, codec->mix[i].reg, 0x3f); 408 j = rdcd(codec, codec->mix[i].reg); 409 wrcd(codec, codec->mix[i].reg, old); 410 codec->mix[i].enable = (j != 0 && j != old)? 1 : 0; 411 for (k = 1; j & (1 << k); k++); 412 codec->mix[i].bits = j? k - codec->mix[i].ofs : 0; 413 } 414 /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ 415 } 416 417 if (bootverbose) { 418 device_printf(codec->dev, "ac97 codec id 0x%08x", id); 419 if (codec->id) 420 printf(" (%s)", codec->id); 421 printf("\n"); 422 device_printf(codec->dev, "ac97 codec features "); 423 for (i = j = 0; i < 10; i++) 424 if (codec->caps & (1 << i)) 425 printf("%s%s", j++? ", " : "", ac97feature[i]); 426 printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 427 printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 428 429 if (codec->extcaps != 0 || codec->extid) { 430 device_printf(codec->dev, "ac97 %s codec", 431 codec->extid? "secondary" : "primary"); 432 if (codec->extcaps) 433 printf(" extended features "); 434 for (i = j = 0; i < 14; i++) 435 if (codec->extcaps & (1 << i)) 436 printf("%s%s", j++? ", " : "", ac97extfeature[i]); 437 printf("\n"); 438 } 439 } 440 441 if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 442 device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 443 snd_mtxunlock(codec->lock); 444 return 0; 445 } 446 447 static unsigned 448 ac97_reinitmixer(struct ac97_info *codec) 449 { 450 unsigned i; 451 452 snd_mtxlock(codec->lock); 453 codec->count = AC97_INIT(codec->methods, codec->devinfo); 454 if (codec->count == 0) { 455 device_printf(codec->dev, "ac97 codec init failed\n"); 456 snd_mtxunlock(codec->lock); 457 return ENODEV; 458 } 459 460 wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 461 wrcd(codec, AC97_REG_RESET, 0); 462 DELAY(100000); 463 wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 464 i = rdcd(codec, AC97_REG_RESET); 465 466 if (!codec->noext) { 467 wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 468 if (rdcd(codec, AC97_REGEXT_STAT) != codec->extstat) 469 device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 470 codec->extstat, rdcd(codec, AC97_REGEXT_STAT)); 471 } 472 473 if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 474 device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 475 snd_mtxunlock(codec->lock); 476 return 0; 477 } 478 479 struct ac97_info * 480 ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 481 { 482 struct ac97_info *codec; 483 484 codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); 485 if (codec == NULL) 486 return NULL; 487 488 snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); 489 codec->lock = snd_mtxcreate(codec->name); 490 codec->methods = kobj_create(cls, M_AC97, M_WAITOK); 491 if (codec->methods == NULL) { 492 snd_mtxlock(codec->lock); 493 snd_mtxfree(codec->lock); 494 free(codec, M_AC97); 495 return NULL; 496 } 497 498 codec->dev = dev; 499 codec->devinfo = devinfo; 500 codec->flags = 0; 501 return codec; 502 } 503 504 void 505 ac97_destroy(struct ac97_info *codec) 506 { 507 snd_mtxlock(codec->lock); 508 if (codec->methods != NULL) 509 kobj_delete(codec->methods, M_AC97); 510 snd_mtxfree(codec->lock); 511 free(codec, M_AC97); 512 } 513 514 void 515 ac97_setflags(struct ac97_info *codec, u_int32_t val) 516 { 517 codec->flags = val; 518 } 519 520 u_int32_t 521 ac97_getflags(struct ac97_info *codec) 522 { 523 return codec->flags; 524 } 525 526 /* -------------------------------------------------------------------- */ 527 528 static int 529 ac97mix_init(struct snd_mixer *m) 530 { 531 struct ac97_info *codec = mix_getdevinfo(m); 532 u_int32_t i, mask; 533 534 if (codec == NULL) 535 return -1; 536 537 if (ac97_initmixer(codec)) 538 return -1; 539 540 mask = 0; 541 for (i = 0; i < 32; i++) 542 mask |= codec->mix[i].enable? 1 << i : 0; 543 mix_setdevs(m, mask); 544 545 mask = 0; 546 for (i = 0; i < 32; i++) 547 mask |= codec->mix[i].recidx? 1 << i : 0; 548 mix_setrecdevs(m, mask); 549 return 0; 550 } 551 552 static int 553 ac97mix_uninit(struct snd_mixer *m) 554 { 555 struct ac97_info *codec = mix_getdevinfo(m); 556 557 if (codec == NULL) 558 return -1; 559 /* 560 if (ac97_uninitmixer(codec)) 561 return -1; 562 */ 563 ac97_destroy(codec); 564 return 0; 565 } 566 567 static int 568 ac97mix_reinit(struct snd_mixer *m) 569 { 570 struct ac97_info *codec = mix_getdevinfo(m); 571 572 if (codec == NULL) 573 return -1; 574 return ac97_reinitmixer(codec); 575 } 576 577 static int 578 ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 579 { 580 struct ac97_info *codec = mix_getdevinfo(m); 581 582 if (codec == NULL) 583 return -1; 584 return ac97_setmixer(codec, dev, left, right); 585 } 586 587 static int 588 ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 589 { 590 int i; 591 struct ac97_info *codec = mix_getdevinfo(m); 592 593 if (codec == NULL) 594 return -1; 595 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 596 if ((src & (1 << i)) != 0) 597 break; 598 return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 599 } 600 601 static kobj_method_t ac97mixer_methods[] = { 602 KOBJMETHOD(mixer_init, ac97mix_init), 603 KOBJMETHOD(mixer_uninit, ac97mix_uninit), 604 KOBJMETHOD(mixer_reinit, ac97mix_reinit), 605 KOBJMETHOD(mixer_set, ac97mix_set), 606 KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 607 { 0, 0 } 608 }; 609 MIXER_DECLARE(ac97mixer); 610 611 /* -------------------------------------------------------------------- */ 612 613 kobj_class_t 614 ac97_getmixerclass(void) 615 { 616 return &ac97mixer_class; 617 } 618 619 620