88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * CDDL HEADER START
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * The contents of this file are subject to the terms of the
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Common Development and Distribution License (the "License").
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * You may not use this file except in compliance with the License.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * or http://www.opensolaris.org/os/licensing.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * See the License for the specific language governing permissions
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * and limitations under the License.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * When distributing Covered Code, include this CDDL HEADER in each
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * If applicable, add the following below this CDDL HEADER, with the
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * fields enclosed by brackets "[]" replaced with your own identifying
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * information: Portions Copyright [yyyy] [name of copyright owner]
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * CDDL HEADER END
239924d360a544a40689b6b360d1183a0a936d97Garrett D'Amore * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore * Copyright 2016 Garrett D'Amore <garrett@damore.org>
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Purpose: Creative/Ensoniq AudioPCI97 driver (ES1371/ES1373)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * This driver is used with the original Ensoniq AudioPCI97 card and many
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * PCI based Sound Blaster cards by Creative Technologies. For example
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Sound Blaster PCI128 and Creative/Ectiva EV1938.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * This file is part of Open Sound System
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Copyright (C) 4Front Technologies 1996-2008.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * This software is released under CDDL 1.0 source license.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * See the COPYING file included in the main directory of this source
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * distribution for the license terms and conditions.
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore * For VMWare platforms, we have to utilize the (emulated) hardware interrupts
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore * of the device. This is necessary for audio playback to function, as
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore * the toggling of the interrupt bits apparently triggers logic inside the
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore * emulated device. So we need to detect this platform, and conditionally
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore * wire up the interrupt handler.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Set the latency to 32, 64, 96, 128 clocks - some APCI97 devices exhibit
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * garbled audio in some cases and setting the latency to higer values fixes it
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Values: 32, 64, 96, 128 - Default: 64 (or defined by bios)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Enable SPDIF port on SoundBlaster 128D or Sound Blaster Digital-4.1 models
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Values: 1=Enable 0=Disable Default: 0
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Note: Latest devices can support SPDIF with AC3 pass thru.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * However, in order to do this, one of the two DMA engines must be
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * dedicated to this, which would prevent the card from supporting 4
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * channel audio. For now we don't bother with the AC3 pass through
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * mode, and instead just focus on 4 channel support. In the future,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * this could be selectable via a property.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Audio parameters */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore 0 /* dma_attr_flags */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore ddi_get8(dev->acch, (uint8_t *)(dev->regs + (offset)))
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore ddi_get16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset)))
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore ddi_get32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset)))
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore ddi_put8(dev->acch, (uint8_t *)(dev->regs + (offset)), v)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore ddi_put16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset)), v)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore ddi_put32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset)), v)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore#define CLR8(dev, offset, v) PUT8(dev, offset, GET8(dev, offset) & ~(v))
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore#define SET8(dev, offset, v) PUT8(dev, offset, GET8(dev, offset) | (v))
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore#define CLR32(dev, offset, v) PUT32(dev, offset, GET32(dev, offset) & ~(v))
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore#define SET32(dev, offset, v) PUT32(dev, offset, GET32(dev, offset) | (v))
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amorestatic void audioens_init_hw(audioens_dev_t *);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* wait for WIP to go away saving the current state for later */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (i = 0; i < 0x100UL; ++i) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* write addr w/data=0 and assert read request ... */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore PUT32(dev, CONC_dCODECCTL_OFF, ((int)wAddr << 16) | (1UL << 23));
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* now wait for the data (RDY) */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (i = 0; i < 0x100UL; ++i) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amoreaudioens_wr97(void *dev_, uint8_t wAddr, uint16_t wData)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* wait for WIP to go away */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (i = 0; i < 0x100UL; ++i) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore PUT32(dev, CONC_dCODECCTL_OFF, ((int)wAddr << 16) | wData);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amorestatic unsigned short
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'AmoreSRCRegRead(audioens_dev_t *dev, unsigned short reg)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* wait for ready */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* assert a read request */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore PUT32(dev, CONC_dSRCIO_OFF, (dtemp & SRC_CTLMASK) | ((int)reg << 25));
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* now wait for the data */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore return ((unsigned short) dtemp);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'AmoreSRCRegWrite(audioens_dev_t *dev, unsigned short reg, unsigned short val)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* wait for ready */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* assert the write request */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore writeval = (dtemp & SRC_CTLMASK) | SRC_WENABLE |
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'AmoreSRCSetRate(audioens_dev_t *dev, unsigned char base, unsigned short rate)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* freeze the channel */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore (GET32(dev, CONC_dSRCIO_OFF) & SRC_CTLMASK) | dtemp);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* calculate new frequency and write it - preserve accum */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore SRCRegWrite(dev, (unsigned short) base + SRC_INT_REGS_OFF,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore (SRCRegRead(dev, (unsigned short) base + SRC_INT_REGS_OFF)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore & 0x00ffU) | ((unsigned short) (freq >> 6) & 0xfc00));
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore SRCRegWrite(dev, (unsigned short) base + SRC_VFREQ_FRAC_OFF,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* un-freeze the channel */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (i = 0; i < SRC_IOPOLL_COUNT; ++i)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore (GET32(dev, CONC_dSRCIO_OFF) & SRC_CTLMASK) & ~dtemp);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* derive oversample ratio */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (N == 15 || N == 13 || N == 11 || N == 9)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* truncate the filter and write n/trunc_start */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* calculate new frequency and write it - preserve accum */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore (SRCRegRead(dev, (unsigned short) base + SRC_INT_REGS_OFF)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore & 0x00ff) | ((unsigned short) (freq >> 6) & 0xfc00));
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Clear all SRC RAM then init - keep SRC disabled until done */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (i = 0; i < 0x80; ++i)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore SRCRegWrite(dev, SRC_DAC1_BASE + SRC_TRUNC_N_OFF, 16 << 4);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore SRCRegWrite(dev, SRC_DAC1_BASE + SRC_INT_REGS_OFF, 16 << 10);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore SRCRegWrite(dev, SRC_DAC2_BASE + SRC_TRUNC_N_OFF, 16 << 4);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore SRCRegWrite(dev, SRC_DAC2_BASE + SRC_INT_REGS_OFF, 16 << 10);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* default some rates */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* now enable the whole deal */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amoreaudioens_writemem(audioens_dev_t *dev, uint32_t page, uint32_t offs,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Select memory page */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amoreaudioens_readmem(audioens_dev_t *dev, uint32_t page, uint32_t offs)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore PUT32(dev, CONC_bMEMPAGE_OFF, page); /* Select memory page */
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amorestatic unsigned
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore /* Three interrupts, DAC1, DAC2, and ADC. The UART we just toss. */
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore /* current frame counter is in high nybble */
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE);
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE);
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore /* current frame counter is in high nybble */
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE);
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE);
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC2IE);
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC2IE);
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore * Consume data in the UART RX FIFO. We don't support
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore * the UART for now, so just eat it.
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore while (GET8(dev, CONC_bUARTCSTAT_OFF) & CONC_UART_RXRDY)
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore if ((ddi_intr_alloc(dev->dip, &dev->intrh, DDI_INTR_TYPE_FIXED, 0, 1,
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore &act, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) || (act != 1)) {
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore audio_dev_warn(dev->osdev, "can't alloc intr handle");
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore if (ddi_intr_get_pri(dev->intrh, &ipri) != DDI_SUCCESS) {
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore audio_dev_warn(dev->osdev, "can't get interrupt priority");
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore if (ddi_intr_add_handler(dev->intrh, audioens_intr, dev, NULL) !=
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore audio_dev_warn(dev->osdev, "cannot add interrupt handler");
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore#endif /* __x86 */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Audio routines
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* hardware can also do AUDIO_FORMAT_U8, but no need for it */
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amoreaudioens_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Set physical address of the DMA buffer */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audioens_writemem(dev, CONC_DAC1CTL_PAGE, CONC_dDAC1PADDR_OFF,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audioens_writemem(dev, CONC_DAC2CTL_PAGE, CONC_dDAC2PADDR_OFF,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore port->paddr + (port->nframes * sizeof (int16_t) * 2));
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Set DAC rate */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore SRCSetRate(dev, SRC_DAC1_BASE, port->speed);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore SRCSetRate(dev, SRC_DAC2_BASE, port->speed);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Configure the channel setup - SPDIF only uses front */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore tmp &= ~(CONC_STATUS_SPKR_MASK | CONC_STATUS_SPDIF_MASK);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore tmp |= CONC_STATUS_SPKR_4CH | CONC_STATUS_SPDIF_P1;
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Set format */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore CONC_PCM_DAC1_STEREO | CONC_PCM_DAC2_STEREO);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Set the frame count */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audioens_writemem(dev, CONC_DAC1CTL_PAGE, CONC_wDAC1FC_OFF,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audioens_writemem(dev, CONC_DAC2CTL_PAGE, CONC_wDAC2FC_OFF,
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore PUT16(dev, CONC_wDAC1IC_OFF, port->iframes - 1);
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore PUT16(dev, CONC_wDAC2IC_OFF, port->iframes - 1);
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Set physical address of the DMA buffer */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audioens_writemem(dev, CONC_ADCCTL_PAGE, CONC_dADCPADDR_OFF,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Set ADC rate */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Set format - for input we only support 16 bit input */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Set the frame count */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audioens_writemem(dev, CONC_ADCCTL_PAGE, CONC_wADCFC_OFF,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Set # of frames between interrupts */
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore PUT16(dev, CONC_wADCIC_OFF, port->iframes - 1);
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN);
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE);
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Note that the current frame counter is in the high nybble.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore frameno = audioens_readmem(port->dev, page, offs) >> 16;
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore (void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORKERNEL);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore (void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amoreaudioens_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if ((port->num == PORT_DAC) && (chan >= 2)) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore (dev->devid == 0x1371 && dev->revision == 7) ||
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore (dev->devid == 0x1371 && dev->revision >= 9)) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Have a ES5880 so enable the codec manually */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (int i = 0; i < 2000; i++)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Turn on CODEC (UART and joystick left disabled)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore tmp &= ~(CONC_DEVCTL_PCICLK_DS | CONC_DEVCTL_XTALCLK_DS);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Perform AC97 codec warm reset */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore PUT8(dev, CONC_bMISCCTL_OFF, tmp | CONC_MISCCTL_SYNC_RES);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* XXX: enable SPDIF - PCM only for now */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* enable SPDIF */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore PUT32(dev, 0x04, GET32(dev, 0x04) | (1 << 18));
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* SPDIF out = data from DAC */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore PUT32(dev, 0x00, GET32(dev, 0x00) | (1 << 26));
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore CLR32(dev, CONC_dSPDIF_OFF, CONC_SPDIF_AC3);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* disable spdif out */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore PUT32(dev, 0x04, GET32(dev, 0x04) & ~(1 << 18));
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore PUT32(dev, 0x00, GET32(dev, 0x00) & ~(1 << 26));
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* we want to run each channel independently */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore CLR32(dev, CONC_dSTATUS_OFF, CONC_STATUS_ECHO);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * On this hardware, we want to disable the internal speaker by
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * default, if it exists. (We don't have a speakerphone on any
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * of these cards, and no SPARC hardware uses it either!)
505c7a699305ccafcfecc1ab0e7d4a25e2bfd1c2Garrett D'Amore (void) ddi_prop_update_int(DDI_DEV_T_NONE, dev->dip, AC97_PROP_SPEAKER,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore dev->ac97 = ac97_alloc(dev->dip, audioens_rd97, audioens_wr97, dev);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (ac97_init(dev->ac97, dev->osdev) != 0) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (int i = 0; i <= PORT_MAX; i++) {
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore * We have 48000Hz. At that rate, 128 frames will give
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore * us an interrupt rate of 375Hz. 2048 frames buys about
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore * 42ms of buffer. Note that interrupts are only enabled
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore * for platforms which need them (i.e. VMWare).
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore dmaflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT;
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore dmaflags = DDI_DMA_READ | DDI_DMA_CONSISTENT;
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore bufsz = port->nframes * port->nchan * sizeof (uint16_t);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Allocate DMA resources.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (ddi_dma_alloc_handle(dev->dip, &dma_attr, DDI_DMA_SLEEP,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore "port %d: dma handle allocation failed", i);
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore if (ddi_dma_mem_alloc(port->dmah, bufsz, &buf_attr,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->kaddr,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore "port %d: dma memory allocation failed", i);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* ensure that the buffer is zeroed out properly */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (ddi_dma_addr_bind_handle(port->dmah, NULL, port->kaddr,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore "port %d: dma binding failed", i);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Allocate and configure audio engine.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore port->engine = audio_engine_alloc(&audioens_engine_ops, caps);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore "port %d: audio_engine_alloc failed", i);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audio_engine_set_private(port->engine, port);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audio_dev_add_engine(dev->osdev, port->engine);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (audio_dev_register(dev->osdev) != DDI_SUCCESS) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore "unable to register with audio framework");
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* free up ports, including DMA resources for ports */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (i = 0; i <= PORT_MAX; i++) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audio_dev_remove_engine(dev->osdev, port->engine);
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audio_dev_warn(dev->osdev, "pci_config_setup failed");
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore vendor = pci_config_get16(pcih, PCI_CONF_VENID);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore device = pci_config_get16(pcih, PCI_CONF_DEVID);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore revision = pci_config_get8(pcih, PCI_CONF_REVID);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if ((vendor != ENSONIQ_VENDOR_ID && vendor != CREATIVE_VENDOR_ID) ||
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore (device != ENSONIQ_ES1371 && device != ENSONIQ_ES5880 &&
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore device != ENSONIQ_ES5880A && device != ECTIVA_ES1938 &&
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore audio_dev_warn(dev->osdev, "unrecognized device");
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audio_dev_set_description(dev->osdev, chip_name);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audio_dev_set_version(dev->osdev, chip_vers);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* set the PCI latency */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if ((audioens_latency == 32) || (audioens_latency == 64) ||
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore pci_config_put8(pcih, PCI_CONF_LATENCY_TIMER,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* activate the device */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore pci_command = pci_config_get16(pcih, PCI_CONF_COMM);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore pci_config_put16(pcih, PCI_CONF_COMM, pci_command);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* map registers */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (ddi_regs_map_setup(dip, 1, &dev->regs, 0, 0, &acc_attr,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audio_dev_warn(dev->osdev, "can't map registers");
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore * Virtual platforms (mostly VMWare!) seem to need us to pulse
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore * the interrupt enables to make progress. So enable (emulated)
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore * hardware interrupts.
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore if (audioens_setup_intr(dev) != DDI_SUCCESS) {
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore /* Reinitialize the mutex with interrupt priority. */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* This allocates and configures the engines */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audio_dev_warn(dev->osdev, "can't init device");
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* first unregister us from the DDI framework, might be busy */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (audio_dev_unregister(dev->osdev) != DDI_SUCCESS)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore ~(CONC_SERCTL_DAC2IE | CONC_SERCTL_DAC1IE | CONC_SERCTL_ADCIE);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* reinitialize hardware */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* restore AC97 state */
5b1627536384deb03449347af9c01bd4fc2d271eGarrett D'Amore CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN | CONC_DEVCTL_ADC_EN);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if ((dev = ddi_get_driver_private(dip)) == NULL) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* This disables all DMA engines and interrupts */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore ~(CONC_SERCTL_DAC2IE | CONC_SERCTL_DAC1IE | CONC_SERCTL_ADCIE);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amoreaudioens_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if ((dev = ddi_get_driver_private(dip)) == NULL) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amoreaudioens_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if ((dev = ddi_get_driver_private(dip)) == NULL) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amorestatic int audioens_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amorestatic int audioens_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore 0, /* refcnt */