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 default: 689 panic("Unknown port number: %d\n", port->num); 690 } 691 692 mutex_enter(&dev->mutex); 693 #ifdef __x86 694 if (!dev->useintr) { 695 #endif 696 697 /* 698 * Note that the current frame counter is in the high nybble. 699 */ 700 frameno = audioens_readmem(port->dev, page, offs) >> 16; 701 n = frameno >= port->frameno ? 702 frameno - port->frameno : 703 frameno + port->nframes - port->frameno; 704 port->frameno = frameno; 705 port->count += n; 706 707 #ifdef __x86 708 } 709 #endif 710 711 val = port->count; 712 mutex_exit(&dev->mutex); 713 714 return (val); 715 } 716 717 static void 718 audioens_close(void *arg) 719 { 720 _NOTE(ARGUNUSED(arg)); 721 } 722 723 static void 724 audioens_sync(void *arg, unsigned nframes) 725 { 726 audioens_port_t *port = arg; 727 728 _NOTE(ARGUNUSED(nframes)); 729 730 if (port->num == PORT_ADC) { 731 (void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORKERNEL); 732 } else { 733 (void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORDEV); 734 } 735 } 736 737 static void 738 audioens_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr) 739 { 740 audioens_port_t *port = arg; 741 742 if ((port->num == PORT_DAC) && (chan >= 2)) { 743 *offset = (port->nframes * 2) + (chan % 2); 744 *incr = 2; 745 } else { 746 *offset = chan; 747 *incr = 2; 748 } 749 } 750 751 audio_engine_ops_t audioens_engine_ops = { 752 AUDIO_ENGINE_VERSION, /* version number */ 753 audioens_open, 754 audioens_close, 755 audioens_start, 756 audioens_stop, 757 audioens_count, 758 audioens_format, 759 audioens_channels, 760 audioens_rate, 761 audioens_sync, 762 NULL, 763 audioens_chinfo, 764 NULL, 765 }; 766 767 void 768 audioens_init_hw(audioens_dev_t *dev) 769 { 770 int tmp; 771 772 if ((dev->devid == ENSONIQ_ES5880) || 773 (dev->devid == ENSONIQ_ES5880A) || 774 (dev->devid == ENSONIQ_ES5880B) || 775 (dev->devid == 0x1371 && dev->revision == 7) || 776 (dev->devid == 0x1371 && dev->revision >= 9)) { 777 778 /* Have a ES5880 so enable the codec manually */ 779 tmp = GET8(dev, CONC_bINTSUMM_OFF) & 0xff; 780 tmp |= 0x20; 781 PUT8(dev, CONC_bINTSUMM_OFF, tmp); 782 for (int i = 0; i < 2000; i++) 783 drv_usecwait(10); 784 } 785 786 SRCInit(dev); 787 788 /* 789 * Turn on CODEC (UART and joystick left disabled) 790 */ 791 tmp = GET32(dev, CONC_bDEVCTL_OFF) & 0xff; 792 tmp &= ~(CONC_DEVCTL_PCICLK_DS | CONC_DEVCTL_XTALCLK_DS); 793 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 794 PUT8(dev, CONC_bUARTCSTAT_OFF, 0x00); 795 796 /* Perform AC97 codec warm reset */ 797 tmp = GET8(dev, CONC_bMISCCTL_OFF) & 0xff; 798 PUT8(dev, CONC_bMISCCTL_OFF, tmp | CONC_MISCCTL_SYNC_RES); 799 drv_usecwait(200); 800 PUT8(dev, CONC_bMISCCTL_OFF, tmp); 801 drv_usecwait(200); 802 803 if (dev->revision >= 4) { 804 /* XXX: enable SPDIF - PCM only for now */ 805 if (audioens_spdif) { 806 /* enable SPDIF */ 807 PUT32(dev, 0x04, GET32(dev, 0x04) | (1 << 18)); 808 /* SPDIF out = data from DAC */ 809 PUT32(dev, 0x00, GET32(dev, 0x00) | (1 << 26)); 810 CLR32(dev, CONC_dSPDIF_OFF, CONC_SPDIF_AC3); 811 812 } else { 813 /* disable spdif out */ 814 PUT32(dev, 0x04, GET32(dev, 0x04) & ~(1 << 18)); 815 PUT32(dev, 0x00, GET32(dev, 0x00) & ~(1 << 26)); 816 } 817 818 /* we want to run each channel independently */ 819 CLR32(dev, CONC_dSTATUS_OFF, CONC_STATUS_ECHO); 820 } 821 } 822 823 static int 824 audioens_init(audioens_dev_t *dev) 825 { 826 827 audioens_init_hw(dev); 828 829 /* 830 * On this hardware, we want to disable the internal speaker by 831 * default, if it exists. (We don't have a speakerphone on any 832 * of these cards, and no SPARC hardware uses it either!) 833 */ 834 (void) ddi_prop_update_int(DDI_DEV_T_NONE, dev->dip, AC97_PROP_SPEAKER, 835 0); 836 837 /* 838 * Init mixer 839 */ 840 841 dev->ac97 = ac97_alloc(dev->dip, audioens_rd97, audioens_wr97, dev); 842 if (dev->ac97 == NULL) 843 return (DDI_FAILURE); 844 845 if (ac97_init(dev->ac97, dev->osdev) != 0) { 846 return (DDI_FAILURE); 847 } 848 849 for (int i = 0; i <= PORT_MAX; i++) { 850 audioens_port_t *port; 851 unsigned caps; 852 unsigned dmaflags; 853 size_t rlen; 854 ddi_dma_cookie_t c; 855 unsigned ccnt; 856 size_t bufsz; 857 858 port = &dev->port[i]; 859 port->dev = dev; 860 861 /* 862 * We have 48000Hz. At that rate, 128 frames will give 863 * us an interrupt rate of 375Hz. 2048 frames buys about 864 * 42ms of buffer. Note that interrupts are only enabled 865 * for platforms which need them (i.e. VMWare). 866 */ 867 868 switch (i) { 869 case PORT_DAC: 870 port->nchan = 4; 871 port->speed = 48000; 872 port->iframes = 128; 873 port->nframes = 2048; 874 caps = ENGINE_OUTPUT_CAP; 875 dmaflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT; 876 break; 877 878 case PORT_ADC: 879 port->nchan = 2; 880 port->speed = 48000; 881 port->iframes = 128; 882 port->nframes = 2048; 883 caps = ENGINE_INPUT_CAP; 884 dmaflags = DDI_DMA_READ | DDI_DMA_CONSISTENT; 885 break; 886 } 887 888 port->num = i; 889 bufsz = port->nframes * port->nchan * sizeof (uint16_t); 890 891 /* 892 * Allocate DMA resources. 893 */ 894 895 if (ddi_dma_alloc_handle(dev->dip, &dma_attr, DDI_DMA_SLEEP, 896 NULL, &port->dmah) != DDI_SUCCESS) { 897 audio_dev_warn(dev->osdev, 898 "port %d: dma handle allocation failed", i); 899 return (DDI_FAILURE); 900 } 901 if (ddi_dma_mem_alloc(port->dmah, bufsz, &buf_attr, 902 DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->kaddr, 903 &rlen, &port->acch) != DDI_SUCCESS) { 904 audio_dev_warn(dev->osdev, 905 "port %d: dma memory allocation failed", i); 906 return (DDI_FAILURE); 907 } 908 /* ensure that the buffer is zeroed out properly */ 909 bzero(port->kaddr, rlen); 910 if (ddi_dma_addr_bind_handle(port->dmah, NULL, port->kaddr, 911 bufsz, dmaflags, DDI_DMA_SLEEP, NULL, 912 &c, &ccnt) != DDI_DMA_MAPPED) { 913 audio_dev_warn(dev->osdev, 914 "port %d: dma binding failed", i); 915 return (DDI_FAILURE); 916 } 917 port->paddr = c.dmac_address; 918 919 /* 920 * Allocate and configure audio engine. 921 */ 922 port->engine = audio_engine_alloc(&audioens_engine_ops, caps); 923 if (port->engine == NULL) { 924 audio_dev_warn(dev->osdev, 925 "port %d: audio_engine_alloc failed", i); 926 return (DDI_FAILURE); 927 } 928 929 audio_engine_set_private(port->engine, port); 930 audio_dev_add_engine(dev->osdev, port->engine); 931 } 932 933 if (audio_dev_register(dev->osdev) != DDI_SUCCESS) { 934 audio_dev_warn(dev->osdev, 935 "unable to register with audio framework"); 936 return (DDI_FAILURE); 937 } 938 939 return (DDI_SUCCESS); 940 } 941 942 void 943 audioens_destroy(audioens_dev_t *dev) 944 { 945 int i; 946 947 #ifdef __x86 948 if (dev->useintr && dev->intrh != NULL) { 949 (void) ddi_intr_disable(dev->intrh); 950 (void) ddi_intr_remove_handler(dev->intrh); 951 (void) ddi_intr_free(dev->intrh); 952 dev->intrh = NULL; 953 } 954 #endif 955 956 mutex_destroy(&dev->mutex); 957 958 /* free up ports, including DMA resources for ports */ 959 for (i = 0; i <= PORT_MAX; i++) { 960 audioens_port_t *port = &dev->port[i]; 961 962 if (port->paddr != 0) 963 (void) ddi_dma_unbind_handle(port->dmah); 964 if (port->acch != NULL) 965 ddi_dma_mem_free(&port->acch); 966 if (port->dmah != NULL) 967 ddi_dma_free_handle(&port->dmah); 968 969 if (port->engine != NULL) { 970 audio_dev_remove_engine(dev->osdev, port->engine); 971 audio_engine_free(port->engine); 972 } 973 } 974 975 if (dev->acch != NULL) { 976 ddi_regs_map_free(&dev->acch); 977 } 978 979 if (dev->ac97) { 980 ac97_free(dev->ac97); 981 } 982 983 if (dev->osdev != NULL) { 984 audio_dev_free(dev->osdev); 985 } 986 987 kmem_free(dev, sizeof (*dev)); 988 } 989 990 int 991 audioens_attach(dev_info_t *dip) 992 { 993 uint16_t pci_command, vendor, device; 994 uint8_t revision; 995 audioens_dev_t *dev; 996 ddi_acc_handle_t pcih; 997 const char *chip_name; 998 const char *chip_vers; 999 1000 dev = kmem_zalloc(sizeof (*dev), KM_SLEEP); 1001 dev->dip = dip; 1002 ddi_set_driver_private(dip, dev); 1003 mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL); 1004 1005 if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) { 1006 audio_dev_warn(dev->osdev, "pci_config_setup failed"); 1007 goto err_exit; 1008 } 1009 1010 vendor = pci_config_get16(pcih, PCI_CONF_VENID); 1011 device = pci_config_get16(pcih, PCI_CONF_DEVID); 1012 revision = pci_config_get8(pcih, PCI_CONF_REVID); 1013 1014 if ((vendor != ENSONIQ_VENDOR_ID && vendor != CREATIVE_VENDOR_ID) || 1015 (device != ENSONIQ_ES1371 && device != ENSONIQ_ES5880 && 1016 device != ENSONIQ_ES5880A && device != ECTIVA_ES1938 && 1017 device != ENSONIQ_ES5880B)) { 1018 audio_dev_warn(dev->osdev, "unrecognized device"); 1019 goto err_exit; 1020 } 1021 1022 chip_name = "AudioPCI97"; 1023 chip_vers = "unknown"; 1024 1025 switch (device) { 1026 case ENSONIQ_ES1371: 1027 chip_name = "AudioPCI97"; 1028 switch (revision) { 1029 case 0x02: 1030 case 0x09: 1031 default: 1032 chip_vers = "ES1371"; 1033 break; 1034 case 0x04: 1035 case 0x06: 1036 case 0x08: 1037 chip_vers = "ES1373"; 1038 break; 1039 case 0x07: 1040 chip_vers = "ES5880"; 1041 break; 1042 } 1043 break; 1044 1045 case ENSONIQ_ES5880: 1046 chip_name = "SB PCI128"; 1047 chip_vers = "ES5880"; 1048 break; 1049 case ENSONIQ_ES5880A: 1050 chip_name = "SB PCI128"; 1051 chip_vers = "ES5880A"; 1052 break; 1053 case ENSONIQ_ES5880B: 1054 chip_name = "SB PCI128"; 1055 chip_vers = "ES5880B"; 1056 break; 1057 1058 case ECTIVA_ES1938: 1059 chip_name = "AudioPCI"; 1060 chip_vers = "ES1938"; 1061 break; 1062 } 1063 1064 dev->revision = revision; 1065 dev->devid = device; 1066 1067 dev->osdev = audio_dev_alloc(dip, 0); 1068 if (dev->osdev == NULL) { 1069 goto err_exit; 1070 } 1071 1072 audio_dev_set_description(dev->osdev, chip_name); 1073 audio_dev_set_version(dev->osdev, chip_vers); 1074 1075 /* set the PCI latency */ 1076 if ((audioens_latency == 32) || (audioens_latency == 64) || 1077 (audioens_latency == 96)) 1078 pci_config_put8(pcih, PCI_CONF_LATENCY_TIMER, 1079 audioens_latency); 1080 1081 /* activate the device */ 1082 pci_command = pci_config_get16(pcih, PCI_CONF_COMM); 1083 pci_command |= PCI_COMM_ME | PCI_COMM_IO; 1084 pci_config_put16(pcih, PCI_CONF_COMM, pci_command); 1085 1086 /* map registers */ 1087 if (ddi_regs_map_setup(dip, 1, &dev->regs, 0, 0, &acc_attr, 1088 &dev->acch) != DDI_SUCCESS) { 1089 audio_dev_warn(dev->osdev, "can't map registers"); 1090 goto err_exit; 1091 } 1092 1093 #ifdef __x86 1094 /* 1095 * Virtual platforms (mostly VMWare!) seem to need us to pulse 1096 * the interrupt enables to make progress. So enable (emulated) 1097 * hardware interrupts. 1098 */ 1099 dev->useintr = B_FALSE; 1100 if (get_hwenv() & HW_VIRTUAL) { 1101 dev->useintr = B_TRUE; 1102 if (audioens_setup_intr(dev) != DDI_SUCCESS) { 1103 goto err_exit; 1104 } 1105 /* Reinitialize the mutex with interrupt priority. */ 1106 mutex_destroy(&dev->mutex); 1107 mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, 1108 DDI_INTR_PRI(dev->intrpri)); 1109 } 1110 #endif 1111 1112 /* This allocates and configures the engines */ 1113 if (audioens_init(dev) != DDI_SUCCESS) { 1114 audio_dev_warn(dev->osdev, "can't init device"); 1115 goto err_exit; 1116 } 1117 1118 #ifdef __x86 1119 if (dev->useintr) { 1120 (void) ddi_intr_enable(dev->intrh); 1121 } 1122 #endif 1123 pci_config_teardown(&pcih); 1124 1125 ddi_report_dev(dip); 1126 1127 return (DDI_SUCCESS); 1128 1129 err_exit: 1130 pci_config_teardown(&pcih); 1131 1132 audioens_destroy(dev); 1133 1134 return (DDI_FAILURE); 1135 } 1136 1137 int 1138 audioens_detach(audioens_dev_t *dev) 1139 { 1140 int tmp; 1141 1142 /* first unregister us from the DDI framework, might be busy */ 1143 if (audio_dev_unregister(dev->osdev) != DDI_SUCCESS) 1144 return (DDI_FAILURE); 1145 1146 mutex_enter(&dev->mutex); 1147 1148 tmp = GET8(dev, CONC_bSERCTL_OFF) & 1149 ~(CONC_SERCTL_DAC2IE | CONC_SERCTL_DAC1IE | CONC_SERCTL_ADCIE); 1150 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1151 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1152 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1153 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1154 1155 tmp = GET8(dev, CONC_bDEVCTL_OFF) & 1156 ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN); 1157 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1158 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1159 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1160 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1161 1162 mutex_exit(&dev->mutex); 1163 1164 audioens_destroy(dev); 1165 1166 return (DDI_SUCCESS); 1167 } 1168 1169 static int 1170 audioens_resume(audioens_dev_t *dev) 1171 { 1172 mutex_enter(&dev->mutex); 1173 dev->suspended = B_FALSE; 1174 mutex_exit(&dev->mutex); 1175 1176 /* reinitialize hardware */ 1177 audioens_init_hw(dev); 1178 1179 /* restore AC97 state */ 1180 ac97_reset(dev->ac97); 1181 1182 audio_dev_resume(dev->osdev); 1183 1184 return (DDI_SUCCESS); 1185 } 1186 1187 static int 1188 audioens_suspend(audioens_dev_t *dev) 1189 { 1190 audio_dev_suspend(dev->osdev); 1191 1192 mutex_enter(&dev->mutex); 1193 CLR8(dev, CONC_bDEVCTL_OFF, 1194 CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN | CONC_DEVCTL_ADC_EN); 1195 dev->suspended = B_TRUE; 1196 mutex_exit(&dev->mutex); 1197 1198 return (DDI_SUCCESS); 1199 } 1200 1201 static int 1202 audioens_quiesce(dev_info_t *dip) 1203 { 1204 audioens_dev_t *dev; 1205 uint8_t tmp; 1206 1207 if ((dev = ddi_get_driver_private(dip)) == NULL) { 1208 return (DDI_FAILURE); 1209 } 1210 1211 /* This disables all DMA engines and interrupts */ 1212 tmp = GET8(dev, CONC_bSERCTL_OFF) & 1213 ~(CONC_SERCTL_DAC2IE | CONC_SERCTL_DAC1IE | CONC_SERCTL_ADCIE); 1214 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1215 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1216 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1217 PUT8(dev, CONC_bSERCTL_OFF, tmp); 1218 1219 tmp = GET8(dev, CONC_bDEVCTL_OFF) & 1220 ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN); 1221 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1222 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1223 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1224 PUT8(dev, CONC_bDEVCTL_OFF, tmp); 1225 1226 return (DDI_SUCCESS); 1227 } 1228 1229 1230 static int 1231 audioens_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 1232 { 1233 audioens_dev_t *dev; 1234 1235 switch (cmd) { 1236 case DDI_ATTACH: 1237 return (audioens_attach(dip)); 1238 1239 case DDI_RESUME: 1240 if ((dev = ddi_get_driver_private(dip)) == NULL) { 1241 return (DDI_FAILURE); 1242 } 1243 return (audioens_resume(dev)); 1244 1245 default: 1246 return (DDI_FAILURE); 1247 } 1248 } 1249 1250 static int 1251 audioens_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 1252 { 1253 audioens_dev_t *dev; 1254 1255 if ((dev = ddi_get_driver_private(dip)) == NULL) { 1256 return (DDI_FAILURE); 1257 } 1258 1259 switch (cmd) { 1260 case DDI_DETACH: 1261 return (audioens_detach(dev)); 1262 1263 case DDI_SUSPEND: 1264 return (audioens_suspend(dev)); 1265 default: 1266 return (DDI_FAILURE); 1267 } 1268 } 1269 1270 static int audioens_ddi_attach(dev_info_t *, ddi_attach_cmd_t); 1271 static int audioens_ddi_detach(dev_info_t *, ddi_detach_cmd_t); 1272 1273 static struct dev_ops audioens_dev_ops = { 1274 DEVO_REV, /* rev */ 1275 0, /* refcnt */ 1276 NULL, /* getinfo */ 1277 nulldev, /* identify */ 1278 nulldev, /* probe */ 1279 audioens_ddi_attach, /* attach */ 1280 audioens_ddi_detach, /* detach */ 1281 nodev, /* reset */ 1282 NULL, /* cb_ops */ 1283 NULL, /* bus_ops */ 1284 NULL, /* power */ 1285 audioens_quiesce, /* quiesce */ 1286 }; 1287 1288 static struct modldrv audioens_modldrv = { 1289 &mod_driverops, /* drv_modops */ 1290 "Ensoniq 1371/1373 Audio", /* linkinfo */ 1291 &audioens_dev_ops, /* dev_ops */ 1292 }; 1293 1294 static struct modlinkage modlinkage = { 1295 MODREV_1, 1296 { &audioens_modldrv, NULL } 1297 }; 1298 1299 int 1300 _init(void) 1301 { 1302 int rv; 1303 1304 audio_init_ops(&audioens_dev_ops, DRVNAME); 1305 if ((rv = mod_install(&modlinkage)) != 0) { 1306 audio_fini_ops(&audioens_dev_ops); 1307 } 1308 return (rv); 1309 } 1310 1311 int 1312 _fini(void) 1313 { 1314 int rv; 1315 1316 if ((rv = mod_remove(&modlinkage)) == 0) { 1317 audio_fini_ops(&audioens_dev_ops); 1318 } 1319 return (rv); 1320 } 1321 1322 int 1323 _info(struct modinfo *modinfop) 1324 { 1325 return (mod_info(&modlinkage, modinfop)); 1326 } 1327