audioens.c revision 88447a05f537aabe9a1bc3d5313f22581ec992a7
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
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Use is subject to license terms.
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.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * The original OSS driver used a single duplex engine and a separate
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * playback only engine. Instead, we expose three engines, one for input
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * and two for output.
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 * The hardware appears to be able to address up to 16-bits worth of longwords,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * giving a total address space of 256K. Note, however, that we will restrict
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * this further when we do fragment and memory allocation. At its very highest
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * clock rate (48 kHz) and sample size (16-bit stereo), and lowest interrupt
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * rate (32 Hz), we only need 6000 bytes per fragment.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * So with an allocated buffer size of 64K, we can support at least 10 frags,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * which is more than enough. (The legacy Sun driver used only 2 fragments.)
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'Amore#define KSINTR(dev) ((kstat_intr_t *)((dev)->ksp->ks_data))
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amorestatic void audioens_init_hw(audioens_dev_t *);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amorestatic void audioens_init_port(audioens_port_t *);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amorestatic void audioens_start_port(audioens_port_t *);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amorestatic void audioens_stop_port(audioens_port_t *);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amorestatic void audioens_update_port(audioens_port_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 */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore unsigned char ackbits = 0;
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * NB: The old audioens didn't report spurious interrupts. On
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * a system with shared interrupts (typical!) there will
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * normally be lots of these (each time the "other" device
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * interrupts).
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Also, because of the way the interrupt chain handling
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * works, reporting of spurious interrupts is probably not
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * terribly useful.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * However, we can count interrupts where the master interrupt
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * bit is set but none of the ackbits that we are prepared to
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * process is set. That is probably useful.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (!(stats & CONC_STATUS_PENDING)) { /* No interrupt pending */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* DAC1 (synth) interrupt */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* DAC2 interrupt */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* ADC interrupt */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* UART interrupt - we shouldn't get this! */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore uint8_t uart_stat = GET8(dev, CONC_bUARTCSTAT_OFF);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Ack the interrupt */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore PUT8(dev, CONC_bSERCTL_OFF, tmp & (~ackbits)); /* Clear bits */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore PUT8(dev, CONC_bSERCTL_OFF, tmp | ackbits); /* Turn them back on */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Audio routines
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* hardware can also do AUDIO_FORMAT_U8, but no need for it */
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,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* Set # of frames between interrupts */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore PUT16(dev, CONC_wDAC1IC_OFF, port->fragfr - 1);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore PUT16(dev, CONC_wDAC2IC_OFF, port->fragfr - 1);
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 */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore PUT16(dev, CONC_wADCIC_OFF, port->fragfr - 1);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amoreaudioens_open(void *arg, int flag, unsigned *fragfrp, unsigned *nfragsp,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* interrupt at least at 25 Hz, and not more than 250 Hz */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore (port->fragfr * port->nchan * sizeof (int16_t));
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore port->nfrags = max(4, min(port->nfrags, 1024));
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore port->nframes = port->nfrags * port->fragfr;
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE);
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;
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore (void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORCPU);
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 PUT8(dev, CONC_bNMIENA_OFF, 0x00); /* NMI off */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore PUT8(dev, CONC_wNMISTAT_OFF, 0x00); /* PUT8? */
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!)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore ddi_prop_update_int(DDI_DEV_T_NONE, dev->dip, AC97_PROP_SPEAKER, 0);
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 dev->pintrs = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore DDI_PROP_DONTPASS, "play-interrupts", DEFINTS);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore dev->rintrs = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore DDI_PROP_DONTPASS, "record-interrupts", DEFINTS);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (int i = 0; i <= PORT_MAX; i++) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore dmaflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT;
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore dmaflags = DDI_DMA_READ | DDI_DMA_CONSISTENT;
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);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (ddi_dma_mem_alloc(port->dmah, AUDIOENS_BUF_LEN, &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 AUDIOENS_BUF_LEN, dmaflags, DDI_DMA_SLEEP, NULL,
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 * Set up kstats for interrupt reporting.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore dev->ksp = kstat_create(ddi_driver_name(dev->dip),
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore ddi_get_instance(dev->dip), ddi_driver_name(dev->dip),
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore "controller", KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (audio_dev_register(dev->osdev) != DDI_SUCCESS) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore "unable to register with audio framework");
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amoreaudioens_setup_interrupts(audioens_dev_t *dev)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if ((ddi_intr_alloc(dev->dip, dev->ihandle, DDI_INTR_TYPE_FIXED,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore 0, 1, &actual, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) ||
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audio_dev_warn(dev->osdev, "can't alloc intr handle");
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (ddi_intr_get_pri(dev->ihandle[0], &ipri) != DDI_SUCCESS) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audio_dev_warn(dev->osdev, "can't determine intr priority");
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (ddi_intr_add_handler(dev->ihandle[0], audioens_intr, dev,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audio_dev_warn(dev->osdev, "can't add intr handler");
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore (void) ddi_intr_remove_handler(dev->ihandle[0]);
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);
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 &&
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");
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (audioens_setup_interrupts(dev) != DDI_SUCCESS) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore audio_dev_warn(dev->osdev, "can't register interrupts");
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 /* ask framework to reset/relocate engine data */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (int i = 0; i <= PORT_MAX; i++) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* reinitialize hardware */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* restore AC97 state */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* restart ports */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (int i = 0; i < PORT_MAX; i++) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* possibly start it up if was going when we suspended */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (int i = 0; i < PORT_MAX; i++) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* signal callbacks on resume */
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Stop all engines/DMA data.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore for (int i = 0; i <= PORT_MAX; i++) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Framework needs to save off AC'97 state.
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 */