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