1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2016 Garrett D'Amore <garrett@damore.org> 24 */ 25 /* 26 * Purpose: Creative/Ensoniq AudioPCI97 driver (ES1371/ES1373) 27 * 28 * This driver is used with the original Ensoniq AudioPCI97 card and many 29 * PCI based Sound Blaster cards by Creative Technologies. For example 30 * Sound Blaster PCI128 and Creative/Ectiva EV1938. 31 */ 32 33 /* 34 * This file is part of Open Sound System 35 * 36 * Copyright (C) 4Front Technologies 1996-2008. 37 * 38 * This software is released under CDDL 1.0 source license. 39 * See the COPYING file included in the main directory of this source 40 * distribution for the license terms and conditions. 41 */ 42 43 #include <sys/audio/audio_driver.h> 44 #include <sys/audio/ac97.h> 45 #include <sys/note.h> 46 #include <sys/pci.h> 47 48 /* 49 * For VMWare platforms, we have to utilize the (emulated) hardware interrupts 50 * of the device. This is necessary for audio playback to function, as 51 * the toggling of the interrupt bits apparently triggers logic inside the 52 * emulated device. So we need to detect this platform, and conditionally 53 * wire up the interrupt handler. 54 */ 55 #ifdef __x86 56 #include <sys/x86_archext.h> 57 #endif 58 59 #include "audioens.h" 60 61 /* 62 * Set the latency to 32, 64, 96, 128 clocks - some APCI97 devices exhibit 63 * garbled audio in some cases and setting the latency to higer values fixes it 64 * Values: 32, 64, 96, 128 - Default: 64 (or defined by bios) 65 */ 66 int audioens_latency = 0; 67 68 /* 69 * Enable SPDIF port on SoundBlaster 128D or Sound Blaster Digital-4.1 models 70 * Values: 1=Enable 0=Disable Default: 0 71 */ 72 int audioens_spdif = 0; 73 74 /* 75 * Note: Latest devices can support SPDIF with AC3 pass thru. 76 * However, in order to do this, one of the two DMA engines must be 77 * dedicated to this, which would prevent the card from supporting 4 78 * channel audio. For now we don't bother with the AC3 pass through 79 * mode, and instead just focus on 4 channel support. In the future, 80 * this could be selectable via a property. 81 */ 82 83 #define ENSONIQ_VENDOR_ID 0x1274 84 #define CREATIVE_VENDOR_ID 0x1102 85 #define ECTIVA_VENDOR_ID 0x1102 86 #define ENSONIQ_ES1371 0x1371 87 #define ENSONIQ_ES5880 0x8001 88 #define ENSONIQ_ES5880A 0x8002 89 #define ENSONIQ_ES5880B 0x5880 90 #define ECTIVA_ES1938 0x8938 91 92 #define DEFRATE 48000 93 #define DRVNAME "audioens" 94 95 typedef struct audioens_port 96 { 97 /* Audio parameters */ 98 int speed; 99 100 int num; 101 #define PORT_DAC 0 102 #define PORT_ADC 1 103 #define PORT_MAX PORT_ADC 104 105 caddr_t kaddr; 106 uint32_t paddr; 107 ddi_acc_handle_t acch; 108 ddi_dma_handle_t dmah; 109 int nchan; 110 unsigned nframes; 111 unsigned iframes; 112 unsigned frameno; 113 uint64_t count; 114 115 struct audioens_dev *dev; 116 audio_engine_t *engine; 117 } audioens_port_t; 118 119 typedef struct audioens_dev 120 { 121 audio_dev_t *osdev; 122 kmutex_t mutex; 123 uint16_t devid; 124 uint8_t revision; 125 dev_info_t *dip; 126 127 audioens_port_t port[PORT_MAX + 1]; 128 129 ac97_t *ac97; 130 131 caddr_t regs; 132 ddi_acc_handle_t acch; 133 134 boolean_t suspended; 135 136 #ifdef __x86 137 boolean_t useintr; 138 ddi_intr_handle_t intrh; 139 uint_t intrpri; 140 #endif 141 } audioens_dev_t; 142 143 static ddi_device_acc_attr_t acc_attr = { 144 DDI_DEVICE_ATTR_V0, 145 DDI_STRUCTURE_LE_ACC, 146 DDI_STRICTORDER_ACC 147 }; 148 149 static ddi_device_acc_attr_t buf_attr = { 150 DDI_DEVICE_ATTR_V0, 151 DDI_NEVERSWAP_ACC, 152 DDI_STRICTORDER_ACC 153 }; 154 155 static ddi_dma_attr_t dma_attr = { 156 DMA_ATTR_VERSION, /* dma_attr_version */ 157 0x0, /* dma_attr_addr_lo */ 158 0xffffffffU, /* dma_attr_addr_hi */ 159 0x3ffff, /* dma_attr_count_max */ 160 0x8, /* dma_attr_align */ 161 0x7f, /* dma_attr_burstsizes */ 162 0x1, /* dma_attr_minxfer */ 163 0x3ffff, /* dma_attr_maxxfer */ 164 0x3ffff, /* dma_attr_seg */ 165 0x1, /* dma_attr_sgllen */ 166 0x1, /* dma_attr_granular */ 167 0 /* dma_attr_flags */ 168 }; 169 170 #define GET8(dev, offset) \ 171 ddi_get8(dev->acch, (uint8_t *)(dev->regs + (offset))) 172 #define GET16(dev, offset) \ 173 ddi_get16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset))) 174 #define GET32(dev, offset) \ 175 ddi_get32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset))) 176 #define PUT8(dev, offset, v) \ 177 ddi_put8(dev->acch, (uint8_t *)(dev->regs + (offset)), v) 178 #define PUT16(dev, offset, v) \ 179 ddi_put16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset)), v) 180 #define PUT32(dev, offset, v) \ 181 ddi_put32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset)), v) 182 183 #define CLR8(dev, offset, v) PUT8(dev, offset, GET8(dev, offset) & ~(v)) 184 #define SET8(dev, offset, v) PUT8(dev, offset, GET8(dev, offset) | (v)) 185 #define CLR32(dev, offset, v) PUT32(dev, offset, GET32(dev, offset) & ~(v)) 186 #define SET32(dev, offset, v) PUT32(dev, offset, GET32(dev, offset) | (v)) 187 188 static void audioens_init_hw(audioens_dev_t *); 189 190 static uint16_t 191 audioens_rd97(void *dev_, uint8_t wAddr) 192 { 193 audioens_dev_t *dev = dev_; 194 int i, dtemp; 195 196 mutex_enter(&dev->mutex); 197 dtemp = GET32(dev, CONC_dCODECCTL_OFF); 198 /* wait for WIP to go away saving the current state for later */ 199 for (i = 0; i < 0x100UL; ++i) { 200 dtemp = GET32(dev, CONC_dCODECCTL_OFF); 201 if ((dtemp & (1UL << 30)) == 0) 202 break; 203 } 204 205 /* write addr w/data=0 and assert read request ... */ 206 PUT32(dev, CONC_dCODECCTL_OFF, ((int)wAddr << 16) | (1UL << 23)); 207 208 /* now wait for the data (RDY) */ 209 for (i = 0; i < 0x100UL; ++i) { 210 dtemp = GET32(dev, CONC_dCODECCTL_OFF); 211 if (dtemp & (1UL << 31)) 212 break; 213 } 214 dtemp = GET32(dev, CONC_dCODECCTL_OFF); 215 mutex_exit(&dev->mutex); 216 217 return (dtemp & 0xffff); 218 } 219 220 static void 221 audioens_wr97(void *dev_, uint8_t wAddr, uint16_t wData) 222 { 223 audioens_dev_t *dev = dev_; 224 int i, dtemp; 225 226 mutex_enter(&dev->mutex); 227 /* wait for WIP to go away */ 228 for (i = 0; i < 0x100UL; ++i) { 229 dtemp = GET32(dev, CONC_dCODECCTL_OFF); 230 if ((dtemp & (1UL << 30)) == 0) 231 break; 232 } 233 234 PUT32(dev, CONC_dCODECCTL_OFF, ((int)wAddr << 16) | wData); 235 236 mutex_exit(&dev->mutex); 237 } 238 239 static unsigned short 240 SRCRegRead(audioens_dev_t *dev, unsigned short reg) 241 { 242 int i, dtemp; 243 244 dtemp = GET32(dev, CONC_dSRCIO_OFF); 245 /* wait for ready */ 246 for (i = 0; i < SRC_IOPOLL_COUNT; ++i) { 247 dtemp = GET32(dev, CONC_dSRCIO_OFF); 248 if ((dtemp & SRC_BUSY) == 0) 249 break; 250 } 251 252 /* assert a read request */ 253 PUT32(dev, CONC_dSRCIO_OFF, (dtemp & SRC_CTLMASK) | ((int)reg << 25)); 254 255 /* now wait for the data */ 256 for (i = 0; i < SRC_IOPOLL_COUNT; ++i) { 257 dtemp = GET32(dev, CONC_dSRCIO_OFF); 258 if ((dtemp & SRC_BUSY) == 0) 259 break; 260 } 261 262 return ((unsigned short) dtemp); 263 } 264 265 static void 266 SRCRegWrite(audioens_dev_t *dev, unsigned short reg, unsigned short val) 267 { 268 int i, dtemp; 269 int writeval; 270 271 dtemp = GET32(dev, CONC_dSRCIO_OFF); 272 /* wait for ready */ 273 for (i = 0; i < SRC_IOPOLL_COUNT; ++i) { 274 dtemp = GET32(dev, CONC_dSRCIO_OFF); 275 if ((dtemp & SRC_BUSY) == 0) 276 break; 277 } 278 279 /* assert the write request */ 280 writeval = (dtemp & SRC_CTLMASK) | SRC_WENABLE | 281 ((int)reg << 25) | val; 282 PUT32(dev, CONC_dSRCIO_OFF, writeval); 283 } 284 285 static void 286 SRCSetRate(audioens_dev_t *dev, unsigned char base, unsigned short rate) 287 { 288 int i, freq, dtemp; 289 unsigned short N, truncM, truncStart; 290 291 if (base != SRC_ADC_BASE) { 292 /* freeze the channel */ 293 dtemp = (base == SRC_DAC1_BASE) ? 294 SRC_DAC1FREEZE : SRC_DAC2FREEZE; 295 for (i = 0; i < SRC_IOPOLL_COUNT; ++i) { 296 if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY)) 297 break; 298 } 299 PUT32(dev, CONC_dSRCIO_OFF, 300 (GET32(dev, CONC_dSRCIO_OFF) & SRC_CTLMASK) | dtemp); 301 302 /* calculate new frequency and write it - preserve accum */ 303 freq = ((int)rate << 16) / 3000U; 304 SRCRegWrite(dev, (unsigned short) base + SRC_INT_REGS_OFF, 305 (SRCRegRead(dev, (unsigned short) base + SRC_INT_REGS_OFF) 306 & 0x00ffU) | ((unsigned short) (freq >> 6) & 0xfc00)); 307 SRCRegWrite(dev, (unsigned short) base + SRC_VFREQ_FRAC_OFF, 308 (unsigned short) freq >> 1); 309 310 /* un-freeze the channel */ 311 for (i = 0; i < SRC_IOPOLL_COUNT; ++i) 312 if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY)) 313 break; 314 PUT32(dev, CONC_dSRCIO_OFF, 315 (GET32(dev, CONC_dSRCIO_OFF) & SRC_CTLMASK) & ~dtemp); 316 } else { 317 /* derive oversample ratio */ 318 N = rate / 3000U; 319 if (N == 15 || N == 13 || N == 11 || N == 9) 320 --N; 321 322 /* truncate the filter and write n/trunc_start */ 323 truncM = (21 * N - 1) | 1; 324 if (rate >= 24000U) { 325 if (truncM > 239) 326 truncM = 239; 327 truncStart = (239 - truncM) >> 1; 328 329 SRCRegWrite(dev, base + SRC_TRUNC_N_OFF, 330 (truncStart << 9) | (N << 4)); 331 } else { 332 if (truncM > 119) 333 truncM = 119; 334 truncStart = (119 - truncM) >> 1; 335 336 SRCRegWrite(dev, base + SRC_TRUNC_N_OFF, 337 0x8000U | (truncStart << 9) | (N << 4)); 338 } 339 340 /* calculate new frequency and write it - preserve accum */ 341 freq = ((48000UL << 16) / rate) * N; 342 SRCRegWrite(dev, base + SRC_INT_REGS_OFF, 343 (SRCRegRead(dev, (unsigned short) base + SRC_INT_REGS_OFF) 344 & 0x00ff) | ((unsigned short) (freq >> 6) & 0xfc00)); 345 SRCRegWrite(dev, base + SRC_VFREQ_FRAC_OFF, 346 (unsigned short) freq >> 1); 347 348 SRCRegWrite(dev, SRC_ADC_VOL_L, N << 8); 349 SRCRegWrite(dev, SRC_ADC_VOL_R, N << 8); 350 } 351 } 352 353 static void 354 SRCInit(audioens_dev_t *dev) 355 { 356 int i; 357 358 /* Clear all SRC RAM then init - keep SRC disabled until done */ 359 for (i = 0; i < SRC_IOPOLL_COUNT; ++i) { 360 if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY)) 361 break; 362 } 363 PUT32(dev, CONC_dSRCIO_OFF, SRC_DISABLE); 364 365 for (i = 0; i < 0x80; ++i) 366 SRCRegWrite(dev, (unsigned short) i, 0U); 367 368 SRCRegWrite(dev, SRC_DAC1_BASE + SRC_TRUNC_N_OFF, 16 << 4); 369 SRCRegWrite(dev, SRC_DAC1_BASE + SRC_INT_REGS_OFF, 16 << 10); 370 SRCRegWrite(dev, SRC_DAC2_BASE + SRC_TRUNC_N_OFF, 16 << 4); 371 SRCRegWrite(dev, SRC_DAC2_BASE + SRC_INT_REGS_OFF, 16 << 10); 372 SRCRegWrite(dev, SRC_DAC1_VOL_L, 1 << 12); 373 SRCRegWrite(dev, SRC_DAC1_VOL_R, 1 << 12); 374 SRCRegWrite(dev, SRC_DAC2_VOL_L, 1 << 12); 375 SRCRegWrite(dev, SRC_DAC2_VOL_R, 1 << 12); 376 SRCRegWrite(dev, SRC_ADC_VOL_L, 1 << 12); 377 SRCRegWrite(dev, SRC_ADC_VOL_R, 1 << 12); 378 379 /* default some rates */ 380 SRCSetRate(dev, SRC_DAC1_BASE, 48000); 381 SRCSetRate(dev, SRC_DAC2_BASE, 48000); 382 SRCSetRate(dev, SRC_ADC_BASE, 48000); 383 384 /* now enable the whole deal */ 385 for (i = 0; i < SRC_IOPOLL_COUNT; ++i) { 386 if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY)) 387 break; 388 } 389 PUT32(dev, CONC_dSRCIO_OFF, 0); 390 } 391 392 static void 393 audioens_writemem(audioens_dev_t *dev, uint32_t page, uint32_t offs, 394 uint32_t data) 395 { 396 /* Select memory page */ 397 PUT32(dev, CONC_bMEMPAGE_OFF, page); 398 PUT32(dev, offs, data); 399 } 400 401 static uint32_t 402 audioens_readmem(audioens_dev_t *dev, uint32_t page, uint32_t offs) 403 { 404 PUT32(dev, CONC_bMEMPAGE_OFF, page); /* Select memory page */ 405 return (GET32(dev, offs)); 406 } 407 408 #ifdef __x86 409 static unsigned 410 audioens_intr(caddr_t arg1, caddr_t arg2) 411 { 412 audioens_dev_t *dev = (void *)arg1; 413 uint32_t status; 414 uint32_t frameno; 415 uint32_t n; 416 audioens_port_t *port; 417 418 _NOTE(ARGUNUSED(arg2)); 419 420 mutex_enter(&dev->mutex); 421 if (dev->suspended || !dev->useintr) { 422 mutex_exit(&dev->mutex); 423 return (DDI_INTR_UNCLAIMED); 424 } 425 426 status = GET32(dev, CONC_dSTATUS_OFF); 427 if ((status & CONC_STATUS_PENDING) == 0) { 428 mutex_exit(&dev->mutex); 429 return (DDI_INTR_UNCLAIMED); 430 } 431 432 /* Three interrupts, DAC1, DAC2, and ADC. The UART we just toss. */ 433 434 if (status & CONC_STATUS_DAC1INT) { 435 port = &dev->port[PORT_DAC]; 436 437 /* current frame counter is in high nybble */ 438 frameno = audioens_readmem(dev, 439 CONC_DAC1CTL_PAGE, CONC_wDAC1FC_OFF) >> 16; 440 n = frameno >= port->frameno ? 441 frameno - port->frameno : 442 frameno + port->nframes - port->frameno; 443 port->frameno = frameno; 444 port->count += n; 445 CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE); 446 SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE); 447 } 448 if (status & CONC_STATUS_ADCINT) { 449 port = &dev->port[PORT_ADC]; 450 451 /* current frame counter is in high nybble */ 452 frameno = audioens_readmem(dev, 453 CONC_ADCCTL_PAGE, CONC_wADCFC_OFF) >> 16; 454 n = frameno >= port->frameno ? 455 frameno - port->frameno : 456 frameno + port->nframes - port->frameno; 457 port->frameno = frameno; 458 port->count += n; 459 CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE); 460 SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE); 461 } 462 if (status & CONC_STATUS_DAC2INT) { 463 CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC2IE); 464 SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC2IE); 465 } 466 if (status & CONC_STATUS_UARTINT) { 467 /* 468 * Consume data in the UART RX FIFO. We don't support 469 * the UART for now, so just eat it. 470 */ 471 while (GET8(dev, CONC_bUARTCSTAT_OFF) & CONC_UART_RXRDY) 472 continue; 473 } 474 mutex_exit(&dev->mutex); 475 476 return (DDI_INTR_CLAIMED); 477 } 478 479 static int 480 audioens_setup_intr(audioens_dev_t *dev) 481 { 482 int act; 483 uint_t ipri; 484 485 if ((ddi_intr_alloc(dev->dip, &dev->intrh, DDI_INTR_TYPE_FIXED, 0, 1, 486 &act, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) || (act != 1)) { 487 audio_dev_warn(dev->osdev, "can't alloc intr handle"); 488 goto fail; 489 } 490 491 if (ddi_intr_get_pri(dev->intrh, &ipri) != DDI_SUCCESS) { 492 audio_dev_warn(dev->osdev, "can't get interrupt priority"); 493 goto fail; 494 } 495 if (ddi_intr_add_handler(dev->intrh, audioens_intr, dev, NULL) != 496 DDI_SUCCESS) { 497 audio_dev_warn(dev->osdev, "cannot add interrupt handler"); 498 goto fail; 499 } 500 dev->intrpri = ipri; 501 return (DDI_SUCCESS); 502 503 fail: 504 if (dev->intrh != NULL) { 505 (void) ddi_intr_free(dev->intrh); 506 dev->intrh = NULL; 507 } 508 return (DDI_FAILURE); 509 } 510 511 #endif /* __x86 */ 512 513 /* 514 * Audio routines 515 */ 516 static int 517 audioens_format(void *arg) 518 { 519 _NOTE(ARGUNUSED(arg)); 520 521 /* hardware can also do AUDIO_FORMAT_U8, but no need for it */ 522 return (AUDIO_FORMAT_S16_LE); 523 } 524 525 static int 526 audioens_channels(void *arg) 527 { 528 audioens_port_t *port = arg; 529 530 return (port->nchan); 531 } 532 533 static int 534 audioens_rate(void *arg) 535 { 536 audioens_port_t *port = arg; 537 538 return (port->speed); 539 } 540 541 static int 542 audioens_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp) 543 { 544 audioens_port_t *port = arg; 545 audioens_dev_t *dev = port->dev; 546 547 _NOTE(ARGUNUSED(flag)); 548 549 mutex_enter(&dev->mutex); 550 551 port->count = 0; 552 553 *nframes = port->nframes; 554 *bufp = port->kaddr; 555 mutex_exit(&dev->mutex); 556 557 return (0); 558 } 559 560 static int 561 audioens_start(void *arg) 562 { 563 audioens_port_t *port = arg; 564 audioens_dev_t *dev = port->dev; 565 uint32_t tmp; 566 567 mutex_enter(&dev->mutex); 568 569 switch (port->num) { 570 case PORT_DAC: 571 /* Set physical address of the DMA buffer */ 572 audioens_writemem(dev, CONC_DAC1CTL_PAGE, CONC_dDAC1PADDR_OFF, 573 port->paddr); 574 audioens_writemem(dev, CONC_DAC2CTL_PAGE, CONC_dDAC2PADDR_OFF, 575 port->paddr + (port->nframes * sizeof (int16_t) * 2)); 576 577 /* Set DAC rate */ 578 SRCSetRate(dev, SRC_DAC1_BASE, port->speed); 579 SRCSetRate(dev, SRC_DAC2_BASE, port->speed); 580 581 /* Configure the channel setup - SPDIF only uses front */ 582 tmp = GET32(dev, CONC_dSTATUS_OFF); 583 tmp &= ~(CONC_STATUS_SPKR_MASK | CONC_STATUS_SPDIF_MASK); 584 tmp |= CONC_STATUS_SPKR_4CH | CONC_STATUS_SPDIF_P1; 585 PUT32(dev, CONC_dSTATUS_OFF, tmp); 586 587 /* Set format */ 588 PUT8(dev, CONC_bSKIPC_OFF, 0x10); 589 SET8(dev, CONC_bSERFMT_OFF, 590 CONC_PCM_DAC1_16BIT | CONC_PCM_DAC2_16BIT | 591 CONC_PCM_DAC1_STEREO | CONC_PCM_DAC2_STEREO); 592 593 /* Set the frame count */ 594 audioens_writemem(dev, CONC_DAC1CTL_PAGE, CONC_wDAC1FC_OFF, 595 port->nframes - 1); 596 audioens_writemem(dev, CONC_DAC2CTL_PAGE, CONC_wDAC2FC_OFF, 597 port->nframes - 1); 598 599 PUT16(dev, CONC_wDAC1IC_OFF, port->iframes - 1); 600 PUT16(dev, CONC_wDAC2IC_OFF, port->iframes - 1); 601 SET8(dev, CONC_bDEVCTL_OFF, 602 CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN); 603 #ifdef __x86 604 if (dev->useintr) { 605 SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE); 606 } 607 #endif 608 609 break; 610 611 case PORT_ADC: 612 /* Set physical address of the DMA buffer */ 613 audioens_writemem(dev, CONC_ADCCTL_PAGE, CONC_dADCPADDR_OFF, 614 port->paddr); 615 616 /* Set ADC rate */ 617 SRCSetRate(dev, SRC_ADC_BASE, port->speed); 618 619 /* Set format - for input we only support 16 bit input */ 620 tmp = GET8(dev, CONC_bSERFMT_OFF); 621 tmp |= CONC_PCM_ADC_16BIT; 622 tmp |= CONC_PCM_ADC_STEREO; 623 624 PUT8(dev, CONC_bSKIPC_OFF, 0x10); 625 626 PUT8(dev, CONC_bSERFMT_OFF, tmp); 627 628 /* Set the frame count */ 629 audioens_writemem(dev, CONC_ADCCTL_PAGE, CONC_wADCFC_OFF, 630 port->nframes - 1); 631 632 /* Set # of frames between interrupts */ 633 PUT16(dev, CONC_wADCIC_OFF, port->iframes - 1); 634 635 SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN); 636 #ifdef __x86 637 if (dev->useintr) { 638 SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE); 639 } 640 #endif 641 break; 642 } 643 644 port->frameno = 0; 645 mutex_exit(&dev->mutex); 646 647 return (0); 648 } 649 650 static void 651 audioens_stop(void *arg) 652 { 653 audioens_port_t *port = arg; 654 audioens_dev_t *dev = port->dev; 655 656 mutex_enter(&dev->mutex); 657 switch (port->num) { 658 case PORT_DAC: 659 CLR8(dev, CONC_bDEVCTL_OFF, 660 CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN); 661 break; 662 case PORT_ADC: 663 CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN); 664 break; 665 } 666 mutex_exit(&dev->mutex); 667 } 668 669 static uint64_t 670 audioens_count(void *arg) 671 { 672 audioens_port_t *port = arg; 673 audioens_dev_t *dev = port->dev; 674 uint64_t val; 675 uint32_t page, offs; 676 int frameno, n; 677 678 switch (port->num) { 679 case PORT_DAC: 680 page = CONC_DAC1CTL_PAGE; 681 offs = CONC_wDAC1FC_OFF; 682 break; 683 684 case PORT_ADC: 685 page = CONC_ADCCTL_PAGE; 686 offs = CONC_wADCFC_OFF; 687 break; 688 } 689 690 mutex_enter(&dev->mutex); 691 #ifdef __x86 692 if (!dev->useintr) { 693 #endif 694 695 /* 696 * Note that the current frame counter is in the high nybble. 697 */ 698 frameno = audioens_readmem(port->dev, page, offs) >> 16; 699 n = frameno >= port->frameno ? 700 frameno - port->frameno : 701 frameno + port->nframes - port->frameno; 702 port->frameno = frameno; 703 port->count += n; 704 705 #ifdef __x86 706 } 707 #endif 708 709 val = port->count; 710 mutex_exit(&dev->mutex); 711 712 return (val); 713 } 714 715 static void 716 audioens_close(void *arg) 717 { 718 _NOTE(ARGUNUSED(arg)); 719 } 720 721 static void 722 audioens_sync(void *arg, unsigned nframes) 723 { 724 audioens_port_t *port = arg; 725 726 _NOTE(ARGUNUSED(nframes)); 727 728 if (port->num == PORT_ADC) { 729 (void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORKERNEL); 730 } else { 731 (void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORDEV); 732 } 733 } 734 735 static void 736 audioens_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr) 737 { 738 audioens_port_t *port = arg; 739 740 if ((port->num == PORT_DAC) && (chan >= 2)) { 741 *offset = (port->nframes * 2) + (chan % 2); 742 *incr = 2; 743 } else { 744 *offset = chan; 745 *incr = 2; 746 } 747 } 748 749 audio_engine_ops_t audioens_engine_ops = { 750 AUDIO_ENGINE_VERSION, /* version number */ 751 audioens_open, 752 audioens_close, 753 audioens_start, 754 audioens_stop, 755 audioens_count, 756 audioens_format, 757 audioens_channels, 758 audioens_rate, 759 audioens_sync, 760 NULL, 761 audioens_chinfo, 762 NULL, 763 }; 764 765 void 766 audioens_init_hw(audioens_dev_t *dev) 767 { 768 int tmp; 769 770 if ((dev->devid == ENSONIQ_ES5880) || 771 (dev->devid == ENSONIQ_ES5880A) || 772 (dev->devid == ENSONIQ_ES5880B) || 773 (dev->devid == 0x1371 && dev->revision == 7) || 774 (dev->devid == 0x1371 && dev->revision >= 9)) { 775 776 /* Have a ES5880 so enable the codec manually */ 777 tmp = GET8(dev, CONC_bINTSUMM_OFF) & 0xff; 778 tmp |= 0x20; 779 PUT8(dev, CONC_bINTSUMM_OFF, tmp); 780 for (int i = 0; i < 2000; i++) 781 drv_usecwait(10); 782 } 783 784 SRCInit(dev); 785 786 /* 787 * Turn on CODEC (UART and joystick left disabled) 788 */ 789 tmp = GET32(dev, CONC_bDEVCTL_OFF) & 0xff; 790 tmp &= ~(CONC_DEVCTL_PCICLK_DS | CONC_DEVCTL_XTALCLK_DS); 791 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 792 PUT8(dev, CONC_bUARTCSTAT_OFF, 0x00); 793 794 /* Perform AC97 codec warm reset */ 795 tmp = GET8(dev, CONC_bMISCCTL_OFF) & 0xff; 796 PUT8(dev, CONC_bMISCCTL_OFF, tmp | CONC_MISCCTL_SYNC_RES); 797 drv_usecwait(200); 798 PUT8(dev, CONC_bMISCCTL_OFF, tmp); 799 drv_usecwait(200); 800 801 if (dev->revision >= 4) { 802 /* XXX: enable SPDIF - PCM only for now */ 803 if (audioens_spdif) { 804 /* enable SPDIF */ 805 PUT32(dev, 0x04, GET32(dev, 0x04) | (1 << 18)); 806 /* SPDIF out = data from DAC */ 807 PUT32(dev, 0x00, GET32(dev, 0x00) | (1 << 26)); 808 CLR32(dev, CONC_dSPDIF_OFF, CONC_SPDIF_AC3); 809 810 } else { 811 /* disable spdif out */ 812 PUT32(dev, 0x04, GET32(dev, 0x04) & ~(1 << 18)); 813 PUT32(dev, 0x00, GET32(dev, 0x00) & ~(1 << 26)); 814 } 815 816 /* we want to run each channel independently */ 817 CLR32(dev, CONC_dSTATUS_OFF, CONC_STATUS_ECHO); 818 } 819 } 820 821 static int 822 audioens_init(audioens_dev_t *dev) 823 { 824 825 audioens_init_hw(dev); 826 827 /* 828 * On this hardware, we want to disable the internal speaker by 829 * default, if it exists. (We don't have a speakerphone on any 830 * of these cards, and no SPARC hardware uses it either!) 831 */ 832 (void) ddi_prop_update_int(DDI_DEV_T_NONE, dev->dip, AC97_PROP_SPEAKER, 833 0); 834 835 /* 836 * Init mixer 837 */ 838 839 dev->ac97 = ac97_alloc(dev->dip, audioens_rd97, audioens_wr97, dev); 840 if (dev->ac97 == NULL) 841 return (DDI_FAILURE); 842 843 if (ac97_init(dev->ac97, dev->osdev) != 0) { 844 return (DDI_FAILURE); 845 } 846 847 for (int i = 0; i <= PORT_MAX; i++) { 848 audioens_port_t *port; 849 unsigned caps; 850 unsigned dmaflags; 851 size_t rlen; 852 ddi_dma_cookie_t c; 853 unsigned ccnt; 854 size_t bufsz; 855 856 port = &dev->port[i]; 857 port->dev = dev; 858 859 /* 860 * We have 48000Hz. At that rate, 128 frames will give 861 * us an interrupt rate of 375Hz. 2048 frames buys about 862 * 42ms of buffer. Note that interrupts are only enabled 863 * for platforms which need them (i.e. VMWare). 864 */ 865 866 switch (i) { 867 case PORT_DAC: 868 port->nchan = 4; 869 port->speed = 48000; 870 port->iframes = 128; 871 port->nframes = 2048; 872 caps = ENGINE_OUTPUT_CAP; 873 dmaflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT; 874 break; 875 876 case PORT_ADC: 877 port->nchan = 2; 878 port->speed = 48000; 879 port->iframes = 128; 880 port->nframes = 2048; 881 caps = ENGINE_INPUT_CAP; 882 dmaflags = DDI_DMA_READ | DDI_DMA_CONSISTENT; 883 break; 884 } 885 886 port->num = i; 887 bufsz = port->nframes * port->nchan * sizeof (uint16_t); 888 889 /* 890 * Allocate DMA resources. 891 */ 892 893 if (ddi_dma_alloc_handle(dev->dip, &dma_attr, DDI_DMA_SLEEP, 894 NULL, &port->dmah) != DDI_SUCCESS) { 895 audio_dev_warn(dev->osdev, 896 "port %d: dma handle allocation failed", i); 897 return (DDI_FAILURE); 898 } 899 if (ddi_dma_mem_alloc(port->dmah, bufsz, &buf_attr, 900 DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->kaddr, 901 &rlen, &port->acch) != DDI_SUCCESS) { 902 audio_dev_warn(dev->osdev, 903 "port %d: dma memory allocation failed", i); 904 return (DDI_FAILURE); 905 } 906 /* ensure that the buffer is zeroed out properly */ 907 bzero(port->kaddr, rlen); 908 if (ddi_dma_addr_bind_handle(port->dmah, NULL, port->kaddr, 909 bufsz, dmaflags, DDI_DMA_SLEEP, NULL, 910 &c, &ccnt) != DDI_DMA_MAPPED) { 911 audio_dev_warn(dev->osdev, 912 "port %d: dma binding failed", i); 913 return (DDI_FAILURE); 914 } 915 port->paddr = c.dmac_address; 916 917 /* 918 * Allocate and configure audio engine. 919 */ 920 port->engine = audio_engine_alloc(&audioens_engine_ops, caps); 921 if (port->engine == NULL) { 922 audio_dev_warn(dev->osdev, 923 "port %d: audio_engine_alloc failed", i); 924 return (DDI_FAILURE); 925 } 926 927 audio_engine_set_private(port->engine, port); 928 audio_dev_add_engine(dev->osdev, port->engine); 929 } 930 931 if (audio_dev_register(dev->osdev) != DDI_SUCCESS) { 932 audio_dev_warn(dev->osdev, 933 "unable to register with audio framework"); 934 return (DDI_FAILURE); 935 } 936 937 return (DDI_SUCCESS); 938 } 939 940 void 941 audioens_destroy(audioens_dev_t *dev) 942 { 943 int i; 944 945 #ifdef __x86 946 if (dev->useintr && dev->intrh != NULL) { 947 (void) ddi_intr_disable(dev->intrh); 948 (void) ddi_intr_remove_handler(dev->intrh); 949 (void) ddi_intr_free(dev->intrh); 950 dev->intrh = NULL; 951 } 952 #endif 953 954 mutex_destroy(&dev->mutex); 955 956 /* free up ports, including DMA resources for ports */ 957 for (i = 0; i <= PORT_MAX; i++) { 958 audioens_port_t *port = &dev->port[i]; 959 960 if (port->paddr != 0) 961 (void) ddi_dma_unbind_handle(port->dmah); 962 if (port->acch != NULL) 963 ddi_dma_mem_free(&port->acch); 964 if (port->dmah != NULL) 965 ddi_dma_free_handle(&port->dmah); 966 967 if (port->engine != NULL) { 968 audio_dev_remove_engine(dev->osdev, port->engine); 969 audio_engine_free(port->engine); 970 } 971 } 972 973 if (dev->acch != NULL) { 974 ddi_regs_map_free(&dev->acch); 975 } 976 977 if (dev->ac97) { 978 ac97_free(dev->ac97); 979 } 980 981 if (dev->osdev != NULL) { 982 audio_dev_free(dev->osdev); 983 } 984 985 kmem_free(dev, sizeof (*dev)); 986 } 987 988 int 989 audioens_attach(dev_info_t *dip) 990 { 991 uint16_t pci_command, vendor, device; 992 uint8_t revision; 993 audioens_dev_t *dev; 994 ddi_acc_handle_t pcih; 995 const char *chip_name; 996 const char *chip_vers; 997 998 dev = kmem_zalloc(sizeof (*dev), KM_SLEEP); 999 dev->dip = dip; 1000 ddi_set_driver_private(dip, dev); 1001 mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL); 1002 1003 if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) { 1004 audio_dev_warn(dev->osdev, "pci_config_setup failed"); 1005 goto err_exit; 1006 } 1007 1008 vendor = pci_config_get16(pcih, PCI_CONF_VENID); 1009 device = pci_config_get16(pcih, PCI_CONF_DEVID); 1010 revision = pci_config_get8(pcih, PCI_CONF_REVID); 1011 1012 if ((vendor != ENSONIQ_VENDOR_ID && vendor != CREATIVE_VENDOR_ID) || 1013 (device != ENSONIQ_ES1371 && device != ENSONIQ_ES5880 && 1014 device != ENSONIQ_ES5880A && device != ECTIVA_ES1938 && 1015 device != ENSONIQ_ES5880B)) { 1016 audio_dev_warn(dev->osdev, "unrecognized device"); 1017 goto err_exit; 1018 } 1019 1020 chip_name = "AudioPCI97"; 1021 chip_vers = "unknown"; 1022 1023 switch (device) { 1024 case ENSONIQ_ES1371: 1025 chip_name = "AudioPCI97"; 1026 switch (revision) { 1027 case 0x02: 1028 case 0x09: 1029 default: 1030 chip_vers = "ES1371"; 1031 break; 1032 case 0x04: 1033 case 0x06: 1034 case 0x08: 1035 chip_vers = "ES1373"; 1036 break; 1037 case 0x07: 1038 chip_vers = "ES5880"; 1039 break; 1040 } 1041 break; 1042 1043 case ENSONIQ_ES5880: 1044 chip_name = "SB PCI128"; 1045 chip_vers = "ES5880"; 1046 break; 1047 case ENSONIQ_ES5880A: 1048 chip_name = "SB PCI128"; 1049 chip_vers = "ES5880A"; 1050 break; 1051 case ENSONIQ_ES5880B: 1052 chip_name = "SB PCI128"; 1053 chip_vers = "ES5880B"; 1054 break; 1055 1056 case ECTIVA_ES1938: 1057 chip_name = "AudioPCI"; 1058 chip_vers = "ES1938"; 1059 break; 1060 } 1061 1062 dev->revision = revision; 1063 dev->devid = device; 1064 1065 dev->osdev = audio_dev_alloc(dip, 0); 1066 if (dev->osdev == NULL) { 1067 goto err_exit; 1068 } 1069 1070 audio_dev_set_description(dev->osdev, chip_name); 1071 audio_dev_set_version(dev->osdev, chip_vers); 1072 1073 /* set the PCI latency */ 1074 if ((audioens_latency == 32) || (audioens_latency == 64) || 1075 (audioens_latency == 96)) 1076 pci_config_put8(pcih, PCI_CONF_LATENCY_TIMER, 1077 audioens_latency); 1078 1079 /* activate the device */ 1080 pci_command = pci_config_get16(pcih, PCI_CONF_COMM); 1081 pci_command |= PCI_COMM_ME | PCI_COMM_IO; 1082 pci_config_put16(pcih, PCI_CONF_COMM, pci_command); 1083 1084 /* map registers */ 1085 if (ddi_regs_map_setup(dip, 1, &dev->regs, 0, 0, &acc_attr, 1086 &dev->acch) != DDI_SUCCESS) { 1087 audio_dev_warn(dev->osdev, "can't map registers"); 1088 goto err_exit; 1089 } 1090 1091 #ifdef __x86 1092 /* 1093 * Virtual platforms (mostly VMWare!) seem to need us to pulse 1094 * the interrupt enables to make progress. So enable (emulated) 1095 * hardware interrupts. 1096 */ 1097 dev->useintr = B_FALSE; 1098 if (get_hwenv() & HW_VIRTUAL) { 1099 dev->useintr = B_TRUE; 1100 if (audioens_setup_intr(dev) != DDI_SUCCESS) { 1101 goto err_exit; 1102 } 1103 /* Reinitialize the mutex with interrupt priority. */ 1104 mutex_destroy(&dev->mutex); 1105 mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, 1106 DDI_INTR_PRI(dev->intrpri)); 1107 } 1108 #endif 1109 1110 /* This allocates and configures the engines */ 1111 if (audioens_init(dev) != DDI_SUCCESS) { 1112 audio_dev_warn(dev->osdev, "can't init device"); 1113 goto err_exit; 1114 } 1115 1116 #ifdef __x86 1117 if (dev->useintr) { 1118 (void) ddi_intr_enable(dev->intrh); 1119 } 1120 #endif 1121 pci_config_teardown(&pcih); 1122 1123 ddi_report_dev(dip); 1124 1125 return (DDI_SUCCESS); 1126 1127 err_exit: 1128 pci_config_teardown(&pcih); 1129 1130 audioens_destroy(dev); 1131 1132 return (DDI_FAILURE); 1133 } 1134 1135 int 1136 audioens_detach(audioens_dev_t *dev) 1137 { 1138 int tmp; 1139 1140 /* first unregister us from the DDI framework, might be busy */ 1141 if (audio_dev_unregister(dev->osdev) != DDI_SUCCESS) 1142 return (DDI_FAILURE); 1143 1144 mutex_enter(&dev->mutex); 1145 1146 tmp = GET8(dev, CONC_bSERCTL_OFF) & 1147 ~(CONC_SERCTL_DAC2IE | CONC_SERCTL_DAC1IE | CONC_SERCTL_ADCIE); 1148 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1149 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1150 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1151 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1152 1153 tmp = GET8(dev, CONC_bDEVCTL_OFF) & 1154 ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN); 1155 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1156 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1157 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1158 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1159 1160 mutex_exit(&dev->mutex); 1161 1162 audioens_destroy(dev); 1163 1164 return (DDI_SUCCESS); 1165 } 1166 1167 static int 1168 audioens_resume(audioens_dev_t *dev) 1169 { 1170 mutex_enter(&dev->mutex); 1171 dev->suspended = B_FALSE; 1172 mutex_exit(&dev->mutex); 1173 1174 /* reinitialize hardware */ 1175 audioens_init_hw(dev); 1176 1177 /* restore AC97 state */ 1178 ac97_reset(dev->ac97); 1179 1180 audio_dev_resume(dev->osdev); 1181 1182 return (DDI_SUCCESS); 1183 } 1184 1185 static int 1186 audioens_suspend(audioens_dev_t *dev) 1187 { 1188 audio_dev_suspend(dev->osdev); 1189 1190 mutex_enter(&dev->mutex); 1191 CLR8(dev, CONC_bDEVCTL_OFF, 1192 CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN | CONC_DEVCTL_ADC_EN); 1193 dev->suspended = B_TRUE; 1194 mutex_exit(&dev->mutex); 1195 1196 return (DDI_SUCCESS); 1197 } 1198 1199 static int 1200 audioens_quiesce(dev_info_t *dip) 1201 { 1202 audioens_dev_t *dev; 1203 uint8_t tmp; 1204 1205 if ((dev = ddi_get_driver_private(dip)) == NULL) { 1206 return (DDI_FAILURE); 1207 } 1208 1209 /* This disables all DMA engines and interrupts */ 1210 tmp = GET8(dev, CONC_bSERCTL_OFF) & 1211 ~(CONC_SERCTL_DAC2IE | CONC_SERCTL_DAC1IE | CONC_SERCTL_ADCIE); 1212 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1213 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1214 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1215 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1216 1217 tmp = GET8(dev, CONC_bDEVCTL_OFF) & 1218 ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN); 1219 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1220 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1221 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1222 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1223 1224 return (DDI_SUCCESS); 1225 } 1226 1227 1228 static int 1229 audioens_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 1230 { 1231 audioens_dev_t *dev; 1232 1233 switch (cmd) { 1234 case DDI_ATTACH: 1235 return (audioens_attach(dip)); 1236 1237 case DDI_RESUME: 1238 if ((dev = ddi_get_driver_private(dip)) == NULL) { 1239 return (DDI_FAILURE); 1240 } 1241 return (audioens_resume(dev)); 1242 1243 default: 1244 return (DDI_FAILURE); 1245 } 1246 } 1247 1248 static int 1249 audioens_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 1250 { 1251 audioens_dev_t *dev; 1252 1253 if ((dev = ddi_get_driver_private(dip)) == NULL) { 1254 return (DDI_FAILURE); 1255 } 1256 1257 switch (cmd) { 1258 case DDI_DETACH: 1259 return (audioens_detach(dev)); 1260 1261 case DDI_SUSPEND: 1262 return (audioens_suspend(dev)); 1263 default: 1264 return (DDI_FAILURE); 1265 } 1266 } 1267 1268 static int audioens_ddi_attach(dev_info_t *, ddi_attach_cmd_t); 1269 static int audioens_ddi_detach(dev_info_t *, ddi_detach_cmd_t); 1270 1271 static struct dev_ops audioens_dev_ops = { 1272 DEVO_REV, /* rev */ 1273 0, /* refcnt */ 1274 NULL, /* getinfo */ 1275 nulldev, /* identify */ 1276 nulldev, /* probe */ 1277 audioens_ddi_attach, /* attach */ 1278 audioens_ddi_detach, /* detach */ 1279 nodev, /* reset */ 1280 NULL, /* cb_ops */ 1281 NULL, /* bus_ops */ 1282 NULL, /* power */ 1283 audioens_quiesce, /* quiesce */ 1284 }; 1285 1286 static struct modldrv audioens_modldrv = { 1287 &mod_driverops, /* drv_modops */ 1288 "Ensoniq 1371/1373 Audio", /* linkinfo */ 1289 &audioens_dev_ops, /* dev_ops */ 1290 }; 1291 1292 static struct modlinkage modlinkage = { 1293 MODREV_1, 1294 { &audioens_modldrv, NULL } 1295 }; 1296 1297 int 1298 _init(void) 1299 { 1300 int rv; 1301 1302 audio_init_ops(&audioens_dev_ops, DRVNAME); 1303 if ((rv = mod_install(&modlinkage)) != 0) { 1304 audio_fini_ops(&audioens_dev_ops); 1305 } 1306 return (rv); 1307 } 1308 1309 int 1310 _fini(void) 1311 { 1312 int rv; 1313 1314 if ((rv = mod_remove(&modlinkage)) == 0) { 1315 audio_fini_ops(&audioens_dev_ops); 1316 } 1317 return (rv); 1318 } 1319 1320 int 1321 _info(struct modinfo *modinfop) 1322 { 1323 return (mod_info(&modlinkage, modinfop)); 1324 } 1325